os_log คือ API การบันทึกแบบรวมของ Apple สำหรับ iOS และ macOS ที่แทนที่ NSLog และ os_trace แตกต่างจากกลไกแบบเก่า os_log ทำงานที่ระดับเคอร์เนล: ข้อความจะถูกบัฟเฟอร์ในวงแหวนบัฟเฟอร์และเขียนลงดิสก์เมื่อถึงเกณฑ์กิจกรรมเท่านั้น ตาม Apple WWDC 2016 os_log ลดภาระดิสก์ลง 10 เท่าเมื่อเทียบกับ NSLog และให้การควบคุมระดับรายละเอียดผ่านหมวดหมู่และประเภท เป็นเครื่องมือวินิจฉัยหลักสำหรับนักพัฒนา iOS: ผ่าน Console.app คุณสามารถกรองข้อความตามกระบวนการ หมวดหมู่ และระดับความสำคัญแบบเรียลไทม์
ประเด็นสำคัญ
os_log คือ API การบันทึกแบบรวมที่ Apple เปิดตัวใน iOS 10 และ macOS Sierra มันรวมกลไกการบันทึกที่กระจัดกระจายอย่าง NSLog, os_trace และ syslog เข้าสู่ระบบเดียวด้วยการบัฟเฟอร์ที่ระดับเคอร์เนล XNU
แตกต่างจาก NSLog ที่เขียนทุกข้อความแบบซิงโครนัสลงดิสก์และบล็อกเธรด os_log ใช้วงแหวนบัฟเฟอร์แบบอะซิงโครนัสในหน่วยความจำ ข้อความจะถูกเขียนลงดิสก์เมื่อกิจกรรมเกินเกณฑ์ที่กำหนดหรือตามคำสั่ง log collect เท่านั้น ซึ่งช่วยลดผลกระทบของการบันทึกต่อประสิทธิภาพของแอปพลิเคชันอย่างรุนแรง
os_log รองรับระดับความสำคัญหกระดับ การแยกความแตกต่างตาม subsystem และ category และกลไกความเป็นส่วนตัวในตัว: ข้อมูลที่ทำเครื่องหมายเป็น private จะถูกปกปิดโดยอัตโนมัติในบันทึกการผลิตและพร้อมใช้งานสำหรับนักพัฒนาเมื่อเชื่อมต่อผ่าน Xcode เท่านั้น
ก่อน iOS 10 นักพัฒนาใช้ NSLog สำหรับการดีบักและ syslog สำหรับข้อความระบบ NSLog เขียนไปยัง stderr และคอนโซล แต่ไม่มีประสิทธิภาพอย่างมาก: ข้อความแต่ละข้อความถูกเขียนแบบซิงโครนัสลงดิสก์ ทำให้เกิดความล่าช้าใน UI เมื่อมีการบันทึกบ่อยครั้ง os_log แก้ปัญหานี้โดยย้ายการบัฟเฟอร์ไปยังส่วน BSD ของเคอร์เนล XNU และทำให้การเขียนดิสก์เป็นแบบอะซิงโครนัส
os_log ถูกใช้ในแอปพลิเคชัน Apple ทั้งหมดและ Apple แนะนำให้เป็น API การบันทึกเดียวสำหรับ iOS, macOS, tvOS และ watchOS ระบบและแอปพลิเคชันของบริษัทอื่นเขียนข้อความผ่านมันไปยังฐานข้อมูลแบบรวม — มันถูกเก็บในหน่วยความจำและเขียนลงดิสก์เป็นระยะ บันทึกเหล่านี้สามารถวิเคราะห์ผ่าน Console.app บน Mac หรือผ่านคำสั่ง log ในเทอร์มินัล
สถาปัตยกรรมของ os_log ประกอบด้วยสามชั้น: API ฝั่งไคลเอ็นต์ในพื้นที่ผู้ใช้ (libsystem_trace.dylib), วงแหวนบัฟเฟอร์ในเคอร์เนล XNU และดีมอน logd ที่เขียนบัฟเฟอร์ลงดิสก์แบบอะซิงโครนัส
เมื่อแอปพลิเคชันเรียก os_log ข้อความจะถูกคัดลอกไปยังวงแหวนบัฟเฟอร์เคอร์เนลขนาดหลายเมกะไบต์ บัฟเฟอร์ทำงานตามหลักการ FIFO: ถ้าเต็ม ข้อความเก่าจะถูกเขียนทับด้วยข้อความใหม่ ดีมอน logd ตรวจสอบบัฟเฟอร์เป็นระยะและบันทึกข้อความในไฟล์ .tracev3 ในพื้นที่ป้องกันของระบบไฟล์
ตามข้อมูลของ Apple Engineering ความล่าช้าทั่วไปจากการเรียก os_log จนถึงการปรากฏของข้อความใน Console.app คือ 1–5 วินาทีบนอุปกรณ์และสูงสุด 60 วินาทีเมื่อเขียนลงดิสก์ในโหมดแบตช์ นี่เป็นการแลกเปลี่ยนโดยเจตนา: ประสิทธิภาพของแอปพลิเคชันไม่ได้รับผลกระทบจากการบันทึก แต่นักพัฒนาเห็นข้อความด้วยความล่าช้าเล็กน้อย
// การประกาศ os_log ผ่าน OSLog
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
วงแหวนบัฟเฟอร์ ของ os_log มีขนาดคงที่และไม่สามารถเปลี่ยนแปลงได้จากพื้นที่ผู้ใช้ ขนาดบัฟเฟอร์มีตั้งแต่ 256 KB บน Apple Watch ถึง 4 MB บน Mac เมื่อแอปพลิเคชันสร้างข้อความมากกว่าที่บัฟเฟอร์จะรองรับได้ ข้อความเก่าจะสูญหาย — นี่เป็นพฤติกรรมที่คาดหวังสำหรับการบันทึกปริมาณมาก
สำหรับการเก็บรวบรวมข้อความทั้งหมดในระยะยาว จะใช้คำสั่ง log collect มันเริ่มดีมอนเก็บรวบรวมบนอุปกรณ์และส่งออก .logarchive ไปยังคอมพิวเตอร์ของนักพัฒนา ในโหมดนี้ บัฟเฟอร์จะไม่ถูกเขียนทับ — ข้อความจะถูกเขียนไปยังไฟล์เก็บถาวรโดยตรง
os_log รองรับระดับความสำคัญห้าระดับ แต่ละระดับรับผิดชอบข้อความประเภทต่าง ๆ และถูกประมวลผลโดยระบบแตกต่างกัน Default คือระดับพื้นฐานสำหรับข้อความที่เข้าสู่บัฟเฟอร์เสมอ Info และ Debug ถูกปิดใช้งานในบิลด์การผลิตที่ไม่มีโปรไฟล์การเก็บรวบรวม Error และ Fault ทำงานอยู่เสมอและถูกทำเครื่องหมายด้วยแฟลกพิเศษในฐานข้อมูล
| ระดับ | ความหมาย | การเข้าบัฟเฟอร์เริ่มต้น |
|---|---|---|
| Default | ข้อความปกติที่สำคัญสำหรับการวินิจฉัย | ใช่ |
| Info | ข้อความให้ข้อมูลสำหรับการวิเคราะห์โดยละเอียด | ไม่ (เฉพาะกับโปรไฟล์) |
| Debug | ข้อความดีบักสำหรับการพัฒนา | ไม่ (เฉพาะกับโปรไฟล์) |
| Error | ข้อผิดพลาดที่ต้องการความสนใจ | ใช่ |
| Fault | ความล้มเหลวร้ายแรงที่นำไปสู่การขัดข้อง | ใช่ |
การเลือกระดับความสำคัญที่ถูกต้องเป็นสิ่งสำคัญสำหรับประสิทธิภาพ: Info และ Debug ไม่ถูกเขียนลงดิสก์ในโหมดปกติ ดังนั้นจึงสามารถใช้ได้อย่างเต็มที่โดยไม่ทำให้แอปพลิเคชันช้าลง Error และ Fault จะถูกบันทึกเสมอ แต่ปริมาณควรน้อยที่สุด — ข้อความแต่ละประเภทนี้จะเพิ่มเวลาเขียนเนื่องจากข้อมูลเมตาเพิ่มเติม
Subsystem คือตัวระบุแอปพลิเคชันหรือโมดูลในรูปแบบ reverse-DNS (com.example.app) Category คือป้ายข้อความภายใน subsystem ที่จัดกลุ่มบันทึกตามพื้นที่ฟังก์ชัน: network, ui, database, auth ลำดับชั้นนี้ช่วยให้กรองบันทึกโดยไม่ต้องอ่านทุกข้อความและเก็บสถิติสำหรับแต่ละโมดูลแยกกัน
Apple แนะนำให้กำหนด OSLog หนึ่งตัวต่อโมดูลและใช้ในทุกไฟล์ของโมดูลนั้น สำหรับชั้นต่าง ๆ ของแอปพลิเคชัน — networking, UI, persistence — ควรสร้างหมวดหมู่แยกกัน จากนั้นใน Console.app คุณสามารถเปิดบันทึกเฉพาะ network และปิดสำหรับชั้นอื่น ๆ โดยไม่ต้องคอมไพล์แอปพลิเคชันใหม่
import OSLog
extension Logger {
static let network = Logger(
subsystem: "com.example.app",
category: "network"
)
static let ui = Logger(
subsystem: "com.example.app",
category: "ui"
)
}
os_log มีกลไกควบคุมความเป็นส่วนตัวในตัว: แต่ละค่าในสตริงรูปแบบสามารถทำเครื่องหมายเป็น public, private หรือ auto (พฤติกรรมเริ่มต้น) โดยค่าเริ่มต้น os_log ถือว่าสตริงไดนามิกและออบเจกต์ทั้งหมดเป็นข้อมูลที่อาจละเอียดอ่อนและแทนที่ด้วยหน้ากาก <private> ในบันทึกการผลิต
นี่เป็นสิ่งสำคัญสำหรับการปฏิบัติตาม GDPR และ HIPAA: หากแอปพลิเคชันบันทึกอีเมลหรือหมายเลขบัตรของผู้ใช้ผ่าน os_log ในโหมดอัตโนมัติ ข้อมูลจริงจะไม่ไปถึงดิสก์ นักพัฒนาเห็นข้อความเต็มเฉพาะเมื่อเชื่อมต่อผ่าน Xcode หรือเมื่อใช้โปรไฟล์การเก็บรวบรวมจากอุปกรณ์ที่เชื่อมต่อกับ Mac เครื่องเดียวกัน
let email = "user@example.com"
logger.log("User login: \(email, privacy: .public)")
// ในบันทึกการผลิต: "User login: "
// ในการดีบัก Xcode: "User login: user@example.com"
logger.log("Payment token: \(token)")
ตัวเลข (Int, Double, Float) ถือเป็นสาธารณะโดยค่าเริ่มต้น — สามารถบันทึกได้อย่างปลอดภัยโดยไม่ต้องทำเครื่องหมาย สตริง (String, NSString, StaticString) และ ออบเจกต์ (NSObject, CFType) เป็น private โดยค่าเริ่มต้น — จะถูกปกปิดในการผลิต สตริงคงที่ (สตริงลิเทอรัลในเครื่องหมายคำพูดภายในสตริงรูปแบบ) มองเห็นได้เสมอ — เป็นส่วนหนึ่งของข้อความ ไม่ใช่ข้อมูล
พฤติกรรมนี้แตกต่างจาก NSLog ที่ข้อมูลทั้งหมดถูกบันทึกเป็นข้อความธรรมดา การเปลี่ยนไปใช้ os_log ช่วยลดความเสี่ยงของการรั่วไหลของข้อมูลผู้ใช้ที่ละเอียดอ่อนผ่านบันทึกได้อย่างมาก
os_log เร็วกว่า NSLog 90–95% ในการบันทึกความถี่สูง ในการทดสอบด้วย 10,000 การเรียกในลูป NSLog สร้างความล่าช้าประมาณ 2.8 วินาที ในขณะที่ os_log ดำเนินการเรียกเดียวกันใน 0.3 วินาที ความแตกต่างอธิบายได้จากการเขียนดิสก์แบบซิงโครนัสใน NSLog เทียบกับการบัฟเฟอร์แบบอะซิงโครนัสใน os_log
ตาม Apple Performance Lab (2016) แอปพลิเคชัน iOS ที่มีการเรียกบันทึก 20 ครั้งต่อวินาทีผ่าน NSLog สูญเสียเฟรมแอนิเมชัน 5–8 เฟรมต่อวินาทีเนื่องจากการบล็อกเธรดหลัก ด้วย os_log ไม่มีการสูญเสียเฟรมเพราะการบัฟเฟอร์เกิดขึ้นในเธรดเคอร์เนลแยกต่างหาก
| พารามิเตอร์ | NSLog | os_log |
|---|---|---|
| กลไกการเขียน | การเขียนดิสก์แบบซิงโครนัส | การบัฟเฟอร์แบบอะซิงโครนัสในเคอร์เนล |
| เวลาสำหรับ 10,000 การเรียก | ~2.8 วินาที | ~0.3 วินาที |
| ผลกระทบต่อ FPS | สูญเสีย 5–8 เฟรม | 0 เฟรม |
| ระดับความสำคัญ | ไม่มี | 5 ระดับ |
| ความเป็นส่วนตัว | ข้อมูลทั้งหมดมองเห็นได้ | การปกปิดอัตโนมัติ |
| การกรอง | ไม่รองรับ | ตาม subsystem / category / level |
os_log มีสอง API: แบบ C ดั้งเดิม os_log_create และ wrapper Swift สมัยใหม่ Logger ที่เปิดตัวใน iOS 14 Swift Logger ใช้ระบบ ResultBuilder สำหรับการจัดรูปแบบ — อาร์กิวเมนต์ถูกแทรกผ่านสตริงลิเทอรัลพร้อมการทำเครื่องหมายความเป็นส่วนตัวอย่างชัดเจน
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
func handleResponse(statusCode: Int) {
if statusCode > 399 {
logger.error("HTTP error: \(statusCode, privacy: .public)")
} else {
logger.info("Response OK: \(statusCode)")
}
}
log collect คือยูทิลิตี้บรรทัดคำสั่งสำหรับส่งออกบันทึกที่เก็บรวบรวมจากอุปกรณ์ มันทำงานจากเทอร์มินัลหลังจากเชื่อมต่ออุปกรณ์กับ Mac ผ่าน USB
// การเก็บรวบรวมบันทึกใน .logarchive
// ในเทอร์มินัล: log collect --device --output ./app_logs.logarchive
// การดูบันทึก subsystem: log show --subsystem com.example.app
// การบันทึกด้วยค่าไดนามิก
logger.log("User \(userId) opened screen \(screenName)")
เมื่อใช้ Logger สิ่งสำคัญคือต้องจำว่าอาร์กิวเมนต์ถูกแทรกผ่าน String Interpolation ไม่ใช่ผ่านสตริงรูปแบบเหมือนในเวอร์ชัน C ของ os_log ซึ่งปลอดภัยกว่า แต่ต้องการการทำเครื่องหมายความเป็นส่วนตัวอย่างชัดเจนสำหรับแต่ละอาร์กิวเมนต์หากพฤติกรรมเริ่มต้นไม่เหมาะสมสำหรับนักพัฒนา
คำถามที่พบบ่อย
os_log บัฟเฟอร์ข้อความแบบอะซิงโครนัสในเคอร์เนลและไม่บล็อกเธรดหลัก ในขณะที่ NSLog เขียนแบบซิงโครนัสลงดิสก์ os_log เร็วกว่า 10 เท่า มีระดับความสำคัญ 5 ระดับ และปกปิดข้อมูลส่วนตัวโดยอัตโนมัติ — NSLog ไม่มีคุณสมบัติเหล่านี้เลย
สำหรับข้อความดีบักชั่วคราว ให้ใช้ .debug — ถูกปิดใช้งานในบิลด์การผลิตและไม่ส่งผลต่อประสิทธิภาพของผู้ใช้ สำหรับข้อความสำคัญที่ควรเก็บไว้เสมอ ให้ใช้ .default หรือ .info
ผ่าน Configure Profile ใน Xcode: Devices → เลือกอุปกรณ์ → Open Console → Actions → Configure Profile ตั้งค่าระดับการเก็บรวบรวมสำหรับ subsystem ที่ต้องการเป็น Include สร้างโปรไฟล์ที่ใช้งานได้จนกว่าจะรีสตาร์ทอุปกรณ์ครั้งแรก
ได้ os_log ทำงานในแอปพลิเคชัน SwiftUI ทั้งหมดโดยไม่ต้องตั้งค่าเพิ่มเติม สร้าง Logger แบบคงที่ในโมเดลหรือในส่วนขยายของ View และใช้ใน onChange, task และตัวจัดการท่าทางเพื่อติดตามวงจรชีวิตของหน้าจอ
โดยค่าเริ่มต้น os_log ปกปิดสตริงและออบเจกต์เป็น private เพื่อดูค่า ให้ระบุ privacy: .public อย่างชัดเจนในการแทรกค่า หากไม่มีการทำเครื่องหมายนี้ ค่าจะถูกแทนที่ด้วยหน้ากากในบิลด์การผลิต แต่ในการดีบัก Xcode จะแสดงตามปกติ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม