Timber เป็นไลบรารีการบันทึกที่มีน้ำหนักเบาสำหรับ Android ที่มีสถาปัตยกรรมที่ขยายได้แบบต้นไม้ (Tree) ซึ่งแทนที่ android.util.Log มาตรฐานในโปรเจกต์นับพัน ตามข้อมูลจาก GitHub, 2024 ไลบรารีได้รับดาวมากกว่า 10,000 ดวงและถูกใช้ในแอปพลิเคชันที่มีการติดตั้งมากกว่า 1 พันล้านครั้ง Timber แก้ปัญหาหลักสามประการของ Log API: การขาด tag อัตโนมัติ, การตรวจสอบ isLoggable ที่จำเป็น และลักษณะคงที่ของการเรียก
ประเด็นสำคัญ
Timber เป็นไลบรารีโอเพนซอร์สสำหรับ Android ที่สร้างโดย Jake Wharton ในปี 2013 เพื่อเป็นทางเลือกแทน android.util.Log มาตรฐาน แนวคิดหลักของ Timber คือการแทนที่ Log API แบบคงที่พร้อม tag ที่ต้องระบุด้วยตนเองด้วยกลไกอัตโนมัติที่กำหนดแหล่งที่มาของการเรียกผ่านสแต็ก
ไลบรารีสร้างขึ้นบนรูปแบบสถาปัตยกรรม Composite กับต้นไม้ (Tree) แทนที่จะเป็นคลาส Log เดียวที่มีพฤติกรรมคงที่ Timber จัดการ "ป่า" ของต้นไม้ — แต่ละต้นไม้รับผิดชอบช่องทางส่งออกของตนเอง: คอนโซล, ไฟล์, Crashlytics, เซิร์ฟเวอร์ระยะไกล นักพัฒนาสามารถเพิ่มต้นไม้จำนวนเท่าใดก็ได้และรวมเข้าด้วยกัน
ตาม Google I/O 2019 Google แนะนำ Timber เป็นแนวทางปฏิบัติที่ดีที่สุดสำหรับการบันทึกในแอปพลิเคชัน Android ไลบรารีใช้พื้นที่น้อยกว่า 10 KB ใน APK และไม่มี dependencies ภายนอก ทำให้เป็นตัวเลือกที่เหมาะสำหรับโปรเจกต์ทุกระดับ
Timber แก้ปัญหา tag ที่ไม่สอดคล้องกันในทีมขนาดใหญ่ เมื่อนักพัฒนาแต่ละคนเขียน tag ด้วยตนเอง ข้อผิดพลาดในการพิมพ์และความไม่สอดคล้องกันเป็นสิ่งที่หลีกเลี่ยงไม่ได้ — คลาสหนึ่งถูกบันทึกเป็น "MainActivity" อีกคลาสเป็น "MAIN_ACTIVITY" Timber จะดึง tag จากชื่อคลาสโดยอัตโนมัติ: MainActivity.kt → tag MainActivity
สถาปัตยกรรม ของ Timber ประกอบด้วยสององค์ประกอบ: คลาสคงที่ส่วนกลาง Timber และคลาสนามธรรม Timber.Tree Timber ทำหน้าที่เป็นส่วนหน้า (facade) ที่มอบหมายการเรียกบันทึกแต่ละครั้งไปยังต้นไม้ที่ปลูกทั้งหมด แต่ละต้นไม้ตัดสินใจว่าจะประมวลผลข้อความหรือไม่ และถ้าใช่ จะส่งไปที่ไหน
DebugTree คือการใช้งาน Tree มาตรฐานที่มาพร้อมกับไลบรารี มันกำหนด tag โดยการวิเคราะห์สแต็กการเรียก: มันขึ้นไป 8 เฟรมจากจุดเรียก Timber.d() และค้นหาชื่อคลาสที่เรียกใช้เมธอดบันทึก DebugTree จะปิดใช้งานโดยอัตโนมัติ (ไม่ส่งออกอะไร) ในบิลด์ release เพราะมันตรวจสอบ BuildConfig.DEBUG
Forest (ป่า) — การรวบรวมต้นไม้ที่ปลูกทั้งหมด เมื่อเรียกใช้เมธอด Timber.d("ข้อความ") ไลบรารีจะส่งข้อความไปยังต้นไม้ทั้งหมดตามลำดับที่ปลูก แต่ละต้นไม้สามารถกรองข้อความตามระดับ tag หรือเนื้อหา และประมวลผลในแบบของตนเอง
ลำดับการปลูกมีความสำคัญ: ต้นไม้ที่ปลูกก่อนจะถูกประมวลผลก่อน ขอแนะนำให้ปลูก DebugTree เป็นลำดับสุดท้าย เพื่อให้ต้นไม้แบบกำหนดเอง (เช่น Crashlytics) ประมวลผลข้อความก่อนที่จะถึง Logcat
Timber เป็นเธรดที่ปลอดภัย — เมธอดทั้งหมดถูกซิงโครไนซ์ผ่านล็อกภายใน สิ่งนี้รับประกันว่าข้อความจากเธรดที่แตกต่างกันจะไม่ปะปนกัน อย่างไรก็ตามภายในต้นไม้แบบกำหนดเอง การซิงโครไนซ์เป็นความรับผิดชอบของนักพัฒนา: หากต้นไม้เขียนไปยังไฟล์ ต้องใช้ synchronized หรือ ReentrantLock
// การเริ่มต้นป่าต้นไม้ใน Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Timber.plant(CrashReportingTree())
Timber.plant(FileLoggingTree())
Timber.i("Timber planted with 3 trees")
}
}
การติดตั้ง Timber ทำได้โดยเพิ่ม dependency เดียวใน build.gradle ไลบรารีเผยแพร่บน Maven Central ภายใต้ artifact com.jakewharton.timber:timber เวอร์ชันปัจจุบัน ณ ปี 2024 คือ 5.0.1 ซึ่งเป็นการอัปเดตเสถียรล่าสุด
// build.gradle (Module: app)
dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
}
การตั้งค่าขั้นต่ำ หลังการติดตั้ง — ปลูก DebugTree ใน Application.onCreate หากไม่มีขั้นตอนนี้ Timber จะละเว้นการเรียกบันทึกทั้งหมดโดยไม่โยนข้อยกเว้น นี่เป็นพฤติกรรมเริ่มต้นที่ปลอดภัย: หากไม่มีต้นไม้ถูกปลูก ไลบรารีจะทำงานว่างเปล่าโดยมีโอเวอร์เฮดน้อยที่สุด
ตาม Jake Wharton, 2023 70% ของปัญหา Timber สำหรับผู้ใช้ใหม่เกี่ยวข้องกับการเริ่มต้นที่ลืมหรือไม่ถูกต้อง Timber ไม่สร้างข้อผิดพลาดเมื่อไม่มีต้นไม้ — นักพัฒนาคาดหวังให้บันทึกปรากฏใน Logcat แต่ไม่มีอะไรเกิดขึ้น
สำหรับการทดสอบ Timber มี Timber.asTree() — เมธอดที่ส่งคืนต้นไม้ปัจจุบันหรือ null สะดวกสำหรับการทดสอบหน่วย: คุณสามารถแทนที่ต้นไม้ด้วย mock และยืนยันว่าข้อความบันทึกถูกส่งด้วยระดับและ tag ที่ถูกต้อง
ต้นไม้แบบกำหนดเอง — เหตุผลหลักในการใช้ Timber แทน Log API มาตรฐาน โดยการแทนที่เมธอดของ Tree คุณสามารถกำหนดเส้นทางบันทึกทุกระดับไปยัง Crashlytics, ระบบไฟล์, Remote Config หรือเซิร์ฟเวอร์ของคุณเอง
class CrashReportingTree : Timber.Tree() {
override fun isLoggable(tag: String?, priority: Int): Boolean {
// เฉพาะ Error และ WTF สำหรับการรายงานข้อขัดข้อง
return priority >= Log.ERROR
}
override fun log(priority: Int, tag: String?,
message: String, t: Throwable?) {
if (t != null) {
FirebaseCrashlytics.getInstance()
.recordException(t)
} else {
FirebaseCrashlytics.getInstance()
.log("[$tag] $message")
}
}
}
เมธอดที่ต้องแทนที่: isLoggable(tag, priority) — ตัวกรองที่กำหนดว่าจะประมวลผลข้อความหรือไม่ (การใช้งานพื้นฐานส่งคืน true) log(priority, tag, message, t) — ตรรกะการประมวลผลหลัก prepareLog(priority, tag, throwable, message, args) — เรียกก่อนการจัดรูปแบบ อนุญาตให้แก้ไขข้อความก่อนการประมวลผล
ข้อได้เปรียบที่สำคัญของต้นไม้แบบกำหนดเองคือ ไม่มีรีเฟลกชัน แตกต่างจากเฟรมเวิร์กการบันทึกหลายตัว Timber ไม่ใช้ Reflection API เพื่อกำหนด tag หรือระดับ Tag ถูกคำนวณผ่านการวิเคราะห์สแต็กการเรียก (Throwable.stackTrace) ซึ่งทำงานเร็วกว่าหลายเท่า
การเปรียบเทียบ Timber และ Log API มาตรฐานแสดงให้เห็นความแตกต่างหลักสี่ประการ: tag อัตโนมัติ, รองรับการจัดรูปแบบสตริงด้วย varargs, ช่องทางส่งออกหลายช่อง และพฤติกรรมที่ปลอดภัยเมื่อไม่ได้เริ่มต้น
| พารามิเตอร์ | android.util.Log | Timber |
|---|---|---|
| การตรวจจับ tag | ด้วยตนเอง, ค่าคงที่สตริง | อัตโนมัติ, ผ่านสแต็กการเรียก |
| การจัดรูปแบบ | การต่อสตริงหรือ String.format | varargs ในตัว + ตัวยึดตำแหน่ง %s |
| ช่องทางส่งออก | Logcat เท่านั้น | ต้นไม้: Logcat, ไฟล์, Crashlytics ฯลฯ |
| พฤติกรรมเมื่อไม่ได้เริ่มต้น | ทำงานเสมอ | ไม่ส่งออกอะไร |
| ประสิทธิภาพ | ระดับพื้นฐาน | การจัดรูปแบบแบบขี้เกียจผ่าน isLoggable |
ข้อโต้แย้งหลักต่อ Timber — การพึ่งพาไลบรารีของบุคคลที่สาม สำหรับโปรเจกต์ธรรมดาที่มีการบันทึกน้อยที่สุด การใช้ Timber อาจมากเกินไป อย่างไรก็ตาม ตาม Google Play Console, 2024 มากกว่า 60% ของ 1,000 แอปอันดับต้น ๆ ใน Google Play ใช้ Timber ซึ่งยืนยันความน่าเชื่อถือและประสิทธิภาพ
ประสิทธิภาพของ Timber ในบิลด์ release เทียบเท่ากับ Log API มาตรฐาน เมื่อไม่มีต้นไม้ถูกปลูก เมธอด Timber.d() จะตรวจสอบการมีอยู่ของต้นไม้ (if เดียว) และส่งคืน — โดยไม่จัดรูปแบบสตริง ซึ่งเร็วกว่า Log.d() ที่มีการต่อสตริงซึ่งทำงานเสมอ
กฎข้อแรก — ตรวจสอบการเริ่มต้น Timber ในทดสอบเสมอ ใช้ Timber.asTree() เพื่อยืนยันว่าต้นไม้ถูกปลูก ในการทดสอบหน่วย ให้ปลูก TestTree ซึ่งบันทึกข้อความในรายการสำหรับการตรวจสอบ assert
กฎข้อที่สอง — อย่าผสม Timber และ android.util.Log ในโปรเจกต์เดียวกัน หากโปรเจกต์ใช้ Timber อยู่แล้ว การเรียกบันทึกใหม่ทั้งหมดควรผ่านมัน การผสมทำให้เกิดข้อความซ้ำและความสับสนในการวิเคราะห์
กฎข้อที่สาม — ปลูก CrashReportingTree โดยไม่ตรวจสอบ BuildConfig.DEBUG ซึ่งแตกต่างจาก DebugTree ต้นไม้ขัดข้องควรทำงานทั้งใน debug และ release — สิ่งนี้ช่วยให้แน่ใจว่าข้อผิดพลาดในการทดสอบจะถูกจับโดยระบบรายงานขัดข้อง
กฎข้อที่สี่ — ใช้ระดับ Timber ในตัว: Timber.v(), Timber.d(), Timber.i(), Timber.w(), Timber.e(), Timber.wtf() หลีกเลี่ยงการเรียก Timber.log() โดยตรงด้วยลำดับความสำคัญที่เป็นตัวเลข — สิ่งนี้ลดความสามารถในการอ่านโค้ดและทำให้การปรับโครงสร้างซับซ้อน
กฎข้อที่ห้า — สำหรับไลบรารีและโมดูล ให้ใช้ Timber.tag("CustomTag") เมธอดนี้ส่งคืนต้นไม้ชั่วคราวที่มี tag ถูกแทนที่โดยไม่ส่งผลต่อการกำหนดค่าส่วนกลาง สิ่งนี้ช่วยให้บันทึกจากโค้ดไลบรารีด้วยตัวระบุที่กำหนดเอง
คำถามที่พบบ่อย
ได้ — Timber ปลอดภัยที่จะใช้ในไลบรารี หากไม่มีต้นไม้ถูกปลูกในแอปพลิเคชัน การเรียก Timber จะไม่ทำให้เกิดข้อผิดพลาด สำหรับไลบรารี ขอแนะนำให้ใช้ Timber.tag("LibraryTag") เพื่อระบุแหล่งที่มาของบันทึก
ผ่านสแต็กการเรียก (stack trace) — DebugTree ขึ้นไป 8 เฟรมจากจุดเรียก Timber.d() และดึงชื่อคลาส เมธอด Throwable.stackTrace ใช้เพื่อกำหนดคลาสที่เรียกโดยไม่มีโอเวอร์เฮดของ Reflection API
Logcat เป็นยูทิลิตี้ระบบ Android สำหรับดูบันทึก Timber เป็นไลบรารีสำหรับเขียนบันทึก Timber ส่งข้อความไปยัง Logcat ผ่าน DebugTree แต่ยังสามารถส่งไปยังไฟล์ Crashlytics Sentry และช่องทางอื่น ๆ ผ่านต้นไม้แบบกำหนดเอง
ไม่ — Timber ผูกพันกับ Android SDK (android.util.Log) สำหรับโปรเจกต์ KMP ให้พิจารณา Kermit หรือ Napier — ไลบรารีการบันทึกข้ามแพลตฟอร์มที่มีสถาปัตยกรรมต้นไม้คล้ายกัน ทำงานบน Android, iOS, JVM และ JS
ใช้ Timber.uprootAll() — เมธอดลบต้นไม้ที่ลงทะเบียนทั้งหมด Timber.uproot(tree) ลบต้นไม้เฉพาะ สิ่งนี้มีประโยชน์ในการทดสอบสำหรับการรีเซ็ตสถานะระหว่างเมธอดทดสอบ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม