你通常如何标记日志条目? (机器人)

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

我的问题是 < strong > 您通常如何在应用程序中标记您的日志? 我见过这样的硬编码:

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

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

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

有没有什么简单的方法可以获得课程标签?

68335 次浏览

You could use this.toString() to get a unique identifer for the specific class in which you print to the log.

I use a TAG, but I initialise it like this:

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

This way when I refactor my code the tag will also change accordingly.

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

I usually use the method name as the tag but from Thread

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

This avoids the new Exception.

At the expense of updating these strings when I move code between methods or rename methods, I like doing the following. Philosophically it also seems to be better to keep "location" or "context" in the tag, not the message.

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


}

The benefit here is that you can filter out a single method even if the content isn't static, e.g.

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

The only drawback is that when I rename f() to g() I need to keep that string in mind. Also, automatic IDE refactoring won't catch these.

For a while I was a fan of using the short class name, I mean LOG_TAG = MyClass.class.getSimpleName(). I found them harder to filter in the logs because there was less to go on.

I usually create an App class that sits in a different package and contains useful static methods. One of the method is a getTag() method, this way I can get the TAG everywhere.
App class looks like this:

EDIT: Improved per br mob comment ( Thanks :) )

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


}

And when I want to use it:

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

The output of the getTag method is the name of the caller class (with the package name), and the line number where the getTag is called from, for easy debuging.

I like to improve Yaniv answer if you have the log in this format (filename.java:XX) xx line number you can link the shortcut the same way gets linked when there's an error, this way I can get direct to the line in question just by click on the logcat

I put this inside my extended Application so i can use in every other file

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

Screenshot:

Go to Android Studio -> preference -> Live Templates -> AndroidLog then select Log.d(TAG, String).

In Template text replace

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

with

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

Image of Android menu

Then click Edit variables and enter className() in the Expression column next to the className Name column. image of Android menu 2

Now when you type the shortcut logd it will put

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

You dont need to define a TAG anymore.

I have created a class of Static variables, methods and classes named as S.

The following is the logging method:

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

It is called in any class as S.L(this, whaterver_object); The getClass().getName() also appends the package name, hence, I am removing it out to avoid making the tag unnecessarily long.

Advantages:

  1. Shorter than Log.d(TAG,
  2. No need to convert int values to their string. Infact no need to type toString
  3. Won't forget to delete Log.d ever as I just have to delete the method and the locations of all logs get marked red.
  4. No need to define TAG at the top of the activity as it takes the name of the class.
  5. The TAG has a prefix of CCC (a short, easy to type string) so that it is easy to list only your logs in android monitor in Android Studio. Sometimes you are running services or other classes simultaneously. If you have to search by activity name alone then you cannot see exactly when a service response was obtained and then an action from your activity has occurred. A prefix like CCC helps as it gives you logs chronologically with the activity in which it occured

AndroidStudio has a logt template by default (you can type logtand press tab to have it expand to a sinppet of code) . I recommend using this to avoid copy pasting the TAG definition from another class and forgetting to change the class you're referring to. The template expands by default to

private static final String TAG = "$CLASS_NAME$"

To avoid using the old class name after refactoring you could change that to

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

Remember to check the "Edit variables" button and make sure that the CLASS_NAME variable is defined to use the className() Expression and has "Skip if defined" checked.

It is a very old question, but even thought an updated answer for July 2018 it is more preferable to use Timber. In order to Log the correct logging, errors and warns can be send to third party crash libraries, such as Firebase or Crashlytics.

In the class that implements Application you should add this:

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

Do not forget Timber dependency.

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

For those users that visit this question:

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

they use Timber for the IOsched app 2019 to show debug info:

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

usage

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


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

note that this makes the Logs available only during DEBUG state and facilitates you the task of removing them manually for the launch on Google Play -

when release the app on the play store, we need to remove all Log statement from the app, so that none of the application data such as user information, hidden application data, auth-tokens are available to user in logcat as plain text

check out this article https://medium.com/mindorks/better-logging-in-android-using-timber-72e40cc2293d

If you're using Kotlin, an extension property on Any can be useful for this:

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

This makes TAG available for any class or object, only needing an import.

I understand that most people use the class name as the tag but I don't really understand this practice. Nowhere have I seen documented what the "tag" conceptually actually is or what it should be used for. But at my company, we are using it differently - to provide information to the logging implementation, such as which file to log to or whether logging can be done asynchronously. For example, a log message might need to be redirected because it's applicable to all customers rather than just the active one, and we may need to force synchronous logging if the application is about to shut down.

The fact that there was originally a 23 character limit on tag names suggests that it was never intended to be used for class names in the first place. If you are using Timber, you can even log the calling class in every log message yourself by using techniques such as Thread.currentThread.stackTrace in your custom Tree (have a look at how Timber does it in DebugTree which is very similar). This doesn't require the use of tags, so you can use tags for other stuff like I mentioned above.