คุณมักจะติดแท็กรายการบันทึกอย่างไร (Android)


98

ฉันถือว่าพวกคุณส่วนใหญ่รู้จัก 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 ฟิลด์ส่วนตัวไว้ในชั้นเรียนของคุณเสมอ

มีวิธีใดในการรับ TAG สำหรับชั้นเรียนหรือไม่?


2
การใช้ TAG แนะนำโดย Android javadocดังนั้นฉันไม่คิดว่ามันจะแย่ไปกว่าการได้ชื่อคลาสที่รันไทม์
Vladimir

ฉันชอบสร้างคลาสเฉพาะเช่น GeneralConstants และใส่แท็กของฉันและฉันสามารถเข้าถึงแท็กของฉันได้ทุกคลาสที่ฉันต้องการเช่นนั้น GeneralConstans.MY_TAG
cagryInside

6
ฉันคิดว่าเป็นการดีที่สุดที่จะกำหนด TAG ในคลาสการเข้ารหัสชื่อคลาสนั้นน่าเกลียด แต่เป็นวิธีเดียวที่เชื่อถือได้ในการทำงานกับ proguard หากคุณไม่เคยใช้ proguard MyActivity.class.getName () เป็นทางออกที่ดีที่สุด หากคุณกังวลเกี่ยวกับชื่อที่ซ้ำกันเพียงแค่ใส่ชื่อแพ็กเกจ การมีชื่อ TAG ในที่อื่นจะกลายเป็นฝันร้ายในการซ่อมบำรุง
Ralph Mueller

คำตอบ:


183

ฉันใช้ TAG แต่เริ่มต้นเป็นดังนี้:

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

ด้วยวิธีนี้เมื่อฉัน refactor โค้ดของฉันแท็กก็จะเปลี่ยนตามไปด้วย


21
ฉันกำลังกำหนดค่าคงที่ของ TAG ในลักษณะเดียวกัน อย่างไรก็ตามฉันสงสัยว่า code obfuscation tools จะส่งผลต่อชื่อคลาสของฉันได้อย่างไรและเป็นผลให้ค่าของค่าคงที่นี้หรือไม่
Olegs Briska

1
"MyActivity.class.getName();"ทุกเวลานี้ฉันได้วางตนเอง ฉันคิดเสมอว่า "TAG" เป็นเพียงตัวยึดตำแหน่งในตัวอย่างจาก Google เป็นต้น ... ไม่ใช่Staticตัวแปรจริง! นี่เป็นทางออกที่ดีกว่ามากขอบคุณ :)
สาย 00

4
ทำไมไม่ลบแบบคงที่และใช้this.getClass().getName()แทนเพื่อให้เป็นแบบทั่วไป
theblang

11
คุณอาจต้องการลองใช้ this.getClass (). getSimpleName () เพื่อหลีกเลี่ยงข้อจำกัดความยาวของ TAG IllegalArgumentException จะถูกโยนหาก tag.length ()> 23
Michael Levy

14
ตามที่กล่าวไว้โดย Ralph Mueller เทคนิคนี้จะใช้ไม่ได้หากคุณใช้ Proguard (เหมือนโครงการ Android ส่วนใหญ่) เพื่อทำให้ชื่อคลาสสับสน
John Patterson

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
Yaniv

2
อาจไม่แนะนำอีกต่อไปเนื่องจากตอนนี้ความยาวแท็กถูก จำกัด ไว้ที่ 23 อักขระ
Claudio Redi

ขอบคุณที่แสดงวิธีการgetStackTrace()ทำงาน แต่ฉันจะไม่ใช้เพราะมันแพง
BlueWizard

13

ไปที่Android สตูดิโอ -> การตั้งค่า -> แม่แบบสด -> 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 เพื่อแสดงฟังก์ชันการทำงานขอบคุณ!
Voy

3
ฉันชอบวิธีนี้ แต่ฉันอยากจะสร้างรายการบันทึกใหม่แทนที่จะแก้ไขรายการบันทึกที่มีอยู่เพื่อให้ปลอดภัยหากมีการเปลี่ยนแปลงในการอัปเดตในอนาคตหรืออะไรบางอย่าง
Alaa

9

ฉันต้องการปรับปรุงคำตอบของ 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;
}

ภาพหน้าจอ:


รักมัน "ขโมย" และอัปเดตคำตอบของฉัน :)
Yaniv

4
อาจไม่แนะนำอีกต่อไปเนื่องจากตอนนี้ความยาวของแท็กถูก จำกัด ไว้ที่ 23 อักขระ
Claudio Redi

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 เป็นสตริง Infact ไม่จำเป็นต้องพิมพ์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สำหรับ instantce)
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 ควรใช้ 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

พวกเขาใช้ 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");

โปรดทราบว่าสิ่งนี้ทำให้ Logs พร้อมใช้งานเฉพาะในสถานะ DEBUG และช่วยให้คุณสามารถลบออกด้วยตนเองสำหรับการเปิดตัวบน Google Play -

เมื่อปล่อยแอพใน play store เราจำเป็นต้องลบคำสั่ง Log ทั้งหมดออกจากแอพเพื่อไม่ให้ข้อมูลแอพพลิเคชั่นเช่นข้อมูลผู้ใช้ข้อมูลแอพพลิเคชั่นที่ซ่อนอยู่โทเค็นการตรวจสอบความถูกต้องมีให้สำหรับผู้ใช้ใน logcat เป็นข้อความธรรมดา

ลองอ่านบทความนี้https://medium.com/mindorks/better-logging-in-android-using-timber-72e40cc2293d


-2

ฉันมักจะใช้ชื่อวิธีการเป็นแท็ก แต่มาจากเธรด

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

สิ่งนี้หลีกเลี่ยงข้อยกเว้นใหม่


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

3
ทำไมต้องสร้างใหม่RuntimeExceptionเพื่อให้ได้ชื่อชั้นปัจจุบัน ที่เลวร้ายมาก.
asgs

นี่คือวิธีที่ฉันแท็กรายการบันทึกของฉันซึ่งเป็นวิธีแก้ปัญหาเดียวที่ฉันสามารถ refactor ได้อย่างถูกต้องเมื่อฉันคัดลอกคลาสจากโครงการไปยังอีกโครงการหนึ่งดังนั้นทำไมไม่ทำ ฉันยินดีรับข้อเสนอแนะหากคุณมีความคิดที่ดีและสะดวกสบายมากขึ้น
เกิดขึ้น

1
หากคุณแค่คัดลอกไฟล์คลาส Java จากตำแหน่งหนึ่งไปยังอีกที่หนึ่งโดยไม่ต้องเปลี่ยนชื่อใด ๆ โซลูชันที่ @gianpi จัดเตรียมไว้ให้คือสิ่งที่จำเป็น มิฉะนั้นคุณสามารถทำได้this.getClass().getName()แม้ว่าคุณจะต้องลบขอบเขตคงที่ของTAG
asgs
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.