Log Level — การจำแนกข้อความบันทึกตามระดับความรุนแรง ช่วยให้นักพัฒนาสามารถควบคุมปริมาณข้อมูลที่แสดงผลในขั้นตอนต่าง ๆ ของการทำงานของแอปพลิเคชัน ตามข้อมูลจาก Google Android Developers, 2024 การเลือกระดับการบันทึกที่ถูกต้องช่วยลดปริมาณล็อกในระบบ production ลง 85–95% และเร่งการวินิจฉัยข้อผิดพลาด แต่ละระดับมีหน้าที่ของตัวเอง — ตั้งแต่การดีบักในขั้นตอนการพัฒนาไปจนถึงการตรวจสอบความล้มเหลวร้ายแรงในระบบ production
ประเด็นสำคัญ
Log Level เป็นคุณสมบัติของข้อความบันทึกแต่ละข้อความที่กำหนดความสำคัญและความเร่งด่วนในการประมวลผล แพลตฟอร์ม iOS และ Android สมัยใหม่รองรับมาตราส่วนแบบรวมตั้งแต่ 6–7 ระดับ: จากละเอียดที่สุด (Verbose/Trace) ถึงร้ายแรง (Error/Assert) การเลือกระดับจะกำหนดว่าข้อความจะถูกเขียนลงในล็อกภายใต้การกำหนดค่าแอปพลิเคชันปัจจุบันหรือไม่
แนวคิดของ Log Level ตั้งอยู่บนหลักการของ พีระมิดความรุนแรง: ยิ่งระดับสูงขึ้นเท่าใด ข้อความที่แสดงในระดับนั้นก็ยิ่งน้อยลงเท่านั้น ตามข้อมูลจาก Semaphore CI, 2024 ในแอปพลิเคชัน production การกระจายเป็นดังนี้: Info — 60% ของข้อความ, Warn — 25%, Error — 10%, Debug — 5% ข้อความ Verbose ควรถูกปิดใช้งานโดยสมบูรณ์ใน production
แต่ละแพลตฟอร์มใช้งาน Log Level ผ่าน API ของตัวเอง Android ใช้ android.util.Log กับเมธอด v(), d(), i(), w(), e() Apple ใช้ OSLog กับระดับ default, info, debug, error, fault ไลบรารีอย่าง Timber และ CocoaLumberjack เพิ่มฟังก์ชันการทำงานเพิ่มเติมเหนือ API มาตรฐานเหล่านี้
ตามข้อมูลจาก Google I/O 2023 การเลือก Log Level ที่ผิดเป็นสาเหตุของ 40% ของปัญหาด้านประสิทธิภาพใน production นักพัฒนาทิ้งล็อก Debug ไว้ในบิลด์เผยแพร่ ซึ่งนำไปสู่การเขียนดิสก์มากเกินไปและการระบายแบตเตอรี่เร็วขึ้น
Verbose (TRACE) — ระดับละเอียดที่สุด สงวนไว้สำหรับการพัฒนาโดยเฉพาะ ในระดับนี้ การคำนวณขั้นกลางทั้งหมด การวนซ้ำ และผลลัพธ์ของแต่ละขั้นตอนของอัลกอริทึมจะถูกแสดงผล บน Android ระดับนี้สอดคล้องกับ Log.v() บน iOS — OSLog ชนิด debug (ก่อน iOS 14 ใช้ os_trace)
Debug — ข้อความดีบักที่มีประโยชน์ระหว่างการพัฒนาและการทดสอบ ประกอบด้วยข้อมูลเกี่ยวกับสถานะของออบเจกต์สำคัญ ผลลัพธ์ของคำสั่ง SQL และพารามิเตอร์การเรียก API แตกต่างจาก Verbose ตรงที่ข้อความ Debug มีโครงสร้างและมีความหมายทางความหมาย บน iOS ระดับนี้สอดคล้องกับ OSLogType.debug
Info — ข้อความแจ้งข้อมูลเกี่ยวกับเหตุการณ์ปกติของแอปพลิเคชัน: การเริ่มต้น SDK, การยืนยันตัวตนสำเร็จ, การเปิดหน้าจอ, การรับข้อมูลจากเซิร์ฟเวอร์ ข้อความ Info ต้องไม่มี ข้อมูลส่วนบุคคลของผู้ใช้และควรปลอดภัยสำหรับการวิเคราะห์ใน production บน iOS ใช้ OSLogType.info บน Android — Log.i()
Warn — คำเตือนเกี่ยวกับปัญหาที่อาจเกิดขึ้น แอปพลิเคชันยังคงทำงานต่อไป แต่สถานการณ์ต้องการความสนใจ: ขนาดแคชใกล้ถึงขีดจำกัด, เวอร์ชัน API ที่ล้าสมัย, การตอบสนองเครือข่ายช้า, การลองเชื่อมต่อใหม่ บน Android — Log.w() บน iOS — OSLogType.default (สำหรับคำเตือน)
Error — ข้อผิดพลาดร้ายแรงที่แอปพลิเคชันไม่สามารถดำเนินการตามที่ร้องขอได้แต่ยังคงทำงานต่อไป: คำขอ API ล้มเหลว, การสูญเสียการเชื่อมต่อ, ข้อผิดพลาดในการเขียนฐานข้อมูล, การขาดสิทธิ์ บน iOS ใช้ OSLogType.error สำหรับข้อผิดพลาด บน Android — Log.e()
Assert (WTF) — ระดับสูงสุด บ่งบอกถึงสถานการณ์ที่ “ไม่สามารถเกิดขึ้นได้” ใช้สำหรับบันทึกบักที่ละเมิดกฎไม่แปรเปลี่ยนพื้นฐานของระบบ บน Android ข้อความ Assert จะไม่แสดงในบิลด์เผยแพร่ตามค่าเริ่มต้น บน iOS WTF (What a Terrible Failure) ถูกจัดการผ่าน OSLogType.fault
Android Log API — กลไกการบันทึกในตัวจากแพ็คเกจ android.util.Log มีเมธอดแบบสแตติก 6 เมธอด: Log.v(), Log.d(), Log.i(), Log.w(), Log.e() และ Log.wtf() แต่ละเมธอดรับ tag (สตริงระบุแหล่งที่มา) และ msg (ข้อความ)
class UserRepository {
companion object {
private val TAG = "UserRepo"
}
suspend fun loadUser(id: String): User {
Log.d(TAG, "กำลังโหลดผู้ใช้ด้วย id: $id")
return try {
val response = api.fetchUser(id)
Log.i(TAG, "โหลดผู้ใช้สำเร็จ")
response.toUser()
} catch (e: Exception) {
Log.e(TAG, "ไม่สามารถโหลดผู้ใช้: ${e.message}")
throw e
}
}
}
การกรองตามระดับ ใน Android Logcat ทำผ่าน ADB: adb logcat *:E จะแสดงเฉพาะข้อความ Error ในบิลด์ production การเรียก Log.v() และ Log.d() ทั้งหมดจะถูกลบโดย ProGuard/R8 เมื่อเปิดใช้งานการย่อขนาด Log.i(), Log.w() และ Log.e() ยังคงอยู่ ดังนั้นจึงสำคัญที่จะไม่แสดงข้อมูลที่ละเอียดอ่อนผ่านเมธอดเหล่านี้
สำหรับการกรองแบบกำหนดเองในขณะรันไทม์ Android มี Log.isLoggable(tag, level) — เมธอดที่ตรวจสอบว่าระดับที่ระบุเปิดใช้งานสำหรับ tag ที่กำหนดหรือไม่ ซึ่งช่วยให้สามารถเปิดใช้งานการบันทึกโดยละเอียดแบบไดนามิกสำหรับโมดูลเฉพาะโดยไม่ต้องสร้างแอปพลิเคชันใหม่
OSLog — ระบบบันทึกแบบรวมของ Apple แทนที่ NSLog ที่เลิกใช้งานแล้ว OSLog มี 5 ระดับ: debug, info, default (notice), error และ fault ข้อได้เปรียบหลักคือ การบันทึกแบบมีโครงสร้าง พร้อมรองรับสตริงที่จัดรูปแบบและการกรองแบบไดนามิกผ่านคอนโซล
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
func fetchData(from url: URL) {
logger.debug("Starting request to \(url.absoluteString)")
do {
let data = try Data(contentsOf: url)
logger.info("Received \(data.count) bytes")
} catch {
logger.error("Request failed: \(error.localizedDescription)")
}
}
ระบบการกรอง ของ OSLog ทำงานในระดับระบบปฏิบัติการ ข้อความ Debug จะถูกเขียนเมื่อมีการเชื่อมต่อดีบักเกอร์หรือเมื่อเปิดใช้งานอาร์กิวเมนต์ -com.apple.CoreData.Logging.debug 1 เท่านั้น ข้อความ Info จะถูกรวบรวมในหน่วยความจำอุปกรณ์ (สูงสุด 512 KB) และเข้าถึงได้ผ่าน Console.app ข้อความ Error และ fault จะถูกเขียนอย่างต่อเนื่องและพร้อมสำหรับการรวบรวมผ่านระบบรายงานข้อขัดข้อง
คุณสมบัติสำคัญของ OSLog: สตริงที่จัดรูปแบบ พร้อมตัวยึดตำแหน่ง แทนที่จะใช้การแทรกสตริงแบบ Swift (ซึ่งถูกประเมินเสมอ โดยไม่คำนึงถึงระดับ) OSLog ใช้ รูปแบบ os_log กับ %{public}@ และ %{private}@ เพื่อแยกข้อมูลที่ละเอียดอ่อน พารามิเตอร์ส่วนตัวถูกปกปิดในล็อก production
กฎหลัก — ชุดระดับขั้นต่ำใน production: Info, Warn, Error, Assert Debug และ Verbose ต้องถูกปิดใช้งาน เหตุผลไม่ใช่เรื่องความปลอดภัยแต่เป็นประสิทธิภาพ: การเรียกบันทึกแต่ละครั้งใช้เวลา CPU ในการจัดรูปแบบสตริง แม้ว่าข้อความจะไม่ถูกแสดงผล
การเพิ่มประสิทธิภาพที่สำคัญ — อย่าใช้การแทรกสตริงในการเรียกบันทึก หากสตริงถูกสร้างขึ้นก่อนการเรียก log() เวลา CPU จะสูญเปล่าแม้ว่าระดับจะถูกปิดใช้งาน ใช้ การจัดรูปแบบแบบขี้เกียจ ผ่านแลมบ์ดาหรือเงื่อนไขป้องกัน
บน Android เมธอด Log.isLoggable() ทำหน้าที่นี้ บน OSLog รองรับสตริงที่จัดรูปแบบดั้งเดิมพร้อมตัวยึดตำแหน่ง Timber สำหรับ Android แก้ปัญหาผ่าน timber.log.Tree พร้อมตรวจสอบระดับภายในทรี
Remote Log Level — แนวทางปฏิบัติที่ระดับการบันทึกถูกควบคุมจากเซิร์ฟเวอร์ผ่าน Firebase Remote Config หรือบริการที่คล้ายกัน หากเกิดข้อผิดพลาดซับซ้อนใน production นักพัฒนาสามารถ เปิดใช้งานการบันทึก Debug จากระยะไกล สำหรับโมดูลเฉพาะบนอุปกรณ์ของกลุ่มผู้ใช้ที่เลือก
ตามข้อมูลจาก Firebase, 2024 แนวทางนี้ลดเวลาในการวินิจฉัยบักที่พบได้ยากลง 60% และช่วยให้เห็นภาพรวมของปัญหาโดยไม่ต้องติดตั้งบิลด์ดีบัก ข้อจำกัดหลัก — การบันทึกจะเปิดใช้งานเมื่อเริ่มต้นแอปพลิเคชันครั้งถัดไปหลังจากได้รับการกำหนดค่าเท่านั้น
BuildConfig.DEBUG บน Android และ #if DEBUG บน Swift เป็นกลไกการคอมไพล์แบบมีเงื่อนไขมาตรฐานที่ปิดใช้งานระดับดีบักในบิลด์เผยแพร่ สำหรับสถาปัตยกรรมที่สะอาด ขอแนะนำให้ย้ายการเลือก Log Level ไปยัง คอนเทนเนอร์ DI หรือโรงงาน logger เพื่อไม่ให้ทำให้ตรรกะทางธุรกิจรกด้วยคำสั่งแบบมีเงื่อนไข
กฎข้อแรก — การเรียกบันทึกแต่ละครั้งควรตอบคำถาม “ใคร, อะไร, เมื่อไหร่” ใคร — คอมโพเนนต์หรือโมดูล (tag บน Android, category บน iOS) อะไร — เหตุการณ์เฉพาะหรือการเปลี่ยนแปลงสถานะ เมื่อไหร่ — ประทับเวลาที่ถูกเพิ่มโดยอัตโนมัติโดยระบบบันทึก
กฎข้อที่สอง — อย่าบันทึกข้อมูลที่ละเอียดอ่อนผ่าน Info และสูงกว่า รหัสผ่าน โทเค็น อีเมล หมายเลขโทรศัพท์ พิกัดทางภูมิศาสตร์ที่แม่นยำ — ห้ามโดยเด็ดขาดในล็อกใด ๆ ที่เข้าสู่ production หากจำเป็นให้ใช้การปกปิด: “email: us***@example.com”
กฎข้อที่สาม — ระดับ Warn เป็นความรับผิดชอบของนักพัฒนา Error — ของทีม Warn หมายถึง “ที่นี่มีปัญหาที่อาจเกิดขึ้น จับตาดูไว้” Error หมายถึง “ที่นี่มีปัญหา แก้ไขมัน” อย่าใช้ Error สำหรับสถานการณ์ที่คาดไว้และถูกจัดการแล้ว (เช่น ข้อผิดพลาด API 404)
กฎข้อที่สี่ — ความสอดคล้อง ทั้งโครงการควรใช้ข้อตกลงการตั้งชื่อแบบรวมสำหรับ tag และหมวดหมู่ ขอแนะนำ ClassName.methodName สำหรับ tag Android และ module.subsystem สำหรับ category iOS ซึ่งช่วยให้กรองล็อกตามคอมโพเนนต์ได้อย่างรวดเร็ว
กฎข้อที่ห้า — ทดสอบล็อกของคุณ ในการทดสอบหน่วย ตรวจสอบว่าเรียก Log Level ที่ถูกต้องในสถานการณ์เฉพาะ มี ไลบรารีจำลองการบันทึก สำหรับจุดประสงค์นี้: Mockito สำหรับ Android, Cuckoo สำหรับ iOS การตรวจสอบระดับในการทดสอบป้องกันการรั่วไหลของข้อความดีบักสู่ production
คำถามที่พบบ่อย
การระบายแบตเตอรี่เร็วขึ้น และการเขียนดิสก์มากเกินไป ล็อก Debug แต่ละรายการจัดรูปแบบสตริงและเขียนข้อมูลลงในบัฟเฟอร์ บนอุปกรณ์ที่มีหน่วยความจำแฟลช สิ่งนี้เร่งการสึกหรอของพื้นที่จัดเก็บ นอกจากนี้ ล็อก Debug อาจมีข้อมูลที่ละเอียดอ่อนที่ไม่ควรเห็นใน production
Debug — สำหรับเนื้อหาคำขอและการตอบกลับ ส่วนหัว และรหัสสถานะ Info — สำหรับข้อเท็จจริงของการดำเนินการคำขอ (URL, เมธอด, ระยะเวลา) Error — สำหรับคำขอที่ล้มเหลวด้วยรหัส 4xx/5xx อย่าใช้ Verbose สำหรับล็อกเครือข่ายใน production
OSLogType.default (ระดับ notice) — ข้อความสำคัญปานกลาง ถูกบันทึกในล็อกระบบและมองเห็นได้ใน Console.app OSLogType.info — ข้อความทางเทคนิค ไม่ถูกบันทึกถาวร สามารถใช้ได้เฉพาะระหว่างการทำโปรไฟล์เชิงรุกผ่าน Instruments
R8/ProGuard ลบ Log.v() และ Log.d() เมื่อเปิดใช้งานการย่อขนาดในบิลด์เผยแพร่ Log.i(), Log.w() และ Log.e() ถูกเก็บรักษาไว้ สำหรับการลบล็อกทั้งหมดโดยสมบูรณ์ จำเป็นต้องมีกฎแบบกำหนดเอง -assumenosideeffects class android.util.Log พร้อมระบุทุกระดับ
ไม่ — การบันทึกมากเกินไปทำให้ความสามารถในการอ่านและประสิทธิภาพลดลง บันทึกการเข้าใช้เฉพาะในเมธอดที่ซับซ้อนหรือไม่พร้อมกัน สำหรับเมธอดแบบซิงโครนัส ล็อกเดียวที่จุดส่งคืนหรือข้อผิดพลาดก็เพียงพอแล้ว ใช้ระดับ Debug สำหรับการติดตามการเรียก
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ