Python logging - แค่ print น่ะ ไม่พอหรอก
เชื่อได้ว่า programmer ทุกคนใช้ print
เป็นตัว debug เช็คค่าตัวแปรนั่นนี่ (ผมก็ทำ) แต่เรามีวิธีที่ดีกว่านั้น
Logging
module เป็น module ที่มาพร้อม Python เลย ไม่ต้องลงเพิ่ม มีความสามารถครบเครื่องเรื่องการ log ต่างๆ ให้เรากลับไปเช็คได้ทีหลัง ไม่ต้องมาดู print
อะไรออกมา ไถๆ ย้อนกลับไปดูหลายบรรทัด และมันยังจัดการบันทึก log ลง file ได้อย่างมีประสิทธิภาพด้วยนะฮะ
นี่เลยเป็นเหตุผลที่ บล็อกนี้เรามาคุยกันเรื่องเจ้า Logging
module ฮะ
แล้วถ้าใครอยากอ่าน official doc ก็ไปตามลิงก์ด้านล่างนี้ได้นะฮะ แต่สำหรับบล็อกนี้ เราจะเน้นกันที่การใช้งานทั่วไปกันก่อน

เริ่มด้วยท่ามาตรฐาน
เราจะเริ่มจาก code ชุดนี้ก่อนนะฮะ
เมื่อเราสร้าง logging
object มาแล้ว เราก็สามารถ log ด้วย 5 level ตามตัวอย่าง + 1 "NOTSET" ที่หมายถึงไม่กำหนด level แต่ผมไม่เคยใช้นะ ไม่มีเหตุต้องใช้
ข้อมูลเพิ่มเติมเกี่ยวกับ level อยู่ที่ลิงก์นี้ฮะ
ผลลัพท์จาก Logging
ข้างต้น จะมีหน้าตาประมาณนี้แหละ

ถ้าเราอยากจะเพิ่มข้อมูลเข้าไป เช่น เวลา หรือ level ให้เห็นชัดๆ ก็สามารถเติม argument format
แบบนี้
มันก็จะแสดงข้อมูลตามรูปแบบที่เรากำหนดเอาไว้เลยฮะ

Handlers
เพิ่มความ advance ไปอีกขั้น ด้วยการกำหนดให้แสดง log ออกมาทั้ง console และ file ตรงนี้ เราจะต้องรู้จักกับ handler กันก่อนฮะ
console
ข้างบน เราใช้ basicConfig()
ใช่มั้ยฮะ แต่รอบนี้ เราหยิบ StreamHandler()
มาใช้แทน
ให้ผลลัพท์คล้ายกันเนอะ แต่ตัวนี้รองรับ argument เพิ่มเติมด้วยนะ เดี๋ยวเราจะเห็นกันต่อไป

file
ทีนี้ พอเราจะ log ลง file เราก็จะใช้ FileHandler
อ่าน file ได้ผลลัพท์แบบนี้เลย

ทำทั้งคู่
ทีนี้ ก็รวมทั้งสองแบบเข้าด้วยกัน แบบนี้
เราก็จะได้ log ทั้งบน console และ file แล้วล่ะ เย่

เพิ่มความซับซ้อนไปอีก
dynamic level
แทนที่เราจะใช้ .info()
หรือ method อื่นๆ ที่กำหนด level เราก็เปลี่ยนมาใช้ .log()
แทนและใส่ค่า level เข้าไปเป็น argument เราก็จะสามารถ dynamic assignment แต่ละ log ว่าอยู่ที่ level ไหนได้แล้วล่ะฮะ
Date time format
วันที่และเวลาอ่านยากจัง เราก็ปรับได้ด้วย argument datefmt
ไปที่ logging.Formatter()
. สำหรับข้อมูล format code ตามอ่านได้ที่ลิงก์นี้เลยฮะ

สร้าง logger หลายตัวพร้อมกัน
ถ้าต้องการสร้าง logger หลายตัว จัดการ log ต่างๆ กันในเวลาเดียวกัน จะทำยังไงได้บ้างนะ
เราสร้าง logger ได้เลยหลายๆ ตัวฮะ แต่ต้องให้แน่ใจก่อนว่า แต่ละตัวจะมีชื่อไม่ซ้ำกัน เรามาดูตัวอย่างกันฮะ
- แรกสุด program เรียก
outer
. outer
สร้าง logger ชื่อ "outer" ไว้เขียนลง file "outer.log" ว่า "[outer] start" กับ "[outer] done"- ระหว่างสองข้อความ
outer
ก็ยังไปเรียกinner
ด้วยการใช้ loop - มีตัวแปร 2 ชุด เลยเรียก
inner
2 ครั้ง - ครั้งแรกส่งข้อความ "abc" ให้
inner
เขียนลง filepath "msg1/inner.log" ซึ่งมันก็จะเติม "[inner]" ไว้ข้างหน้าข้อความ - ครั้งที่สองส่ง "def" ให้
inner
เขียนลง filepath "msg2/inner.log" พร้อมกับเติม "[inner]" เหมือนกัน inner
กำหนด logger ชื่อ "inner".
ดูง่ายๆ ไม่มีอะไรซับซ้อน แต่ผลลัพท์ผิดนะฮะ เพราะ file "msg1/inner.log" แทนที่จะมีแค่ "[inner] abc" กลับมี "[inner] def" เพิ่มเข้ามา

จุดที่หลุดไป คือ เราตั้งชื่อ log ของ inner
ซ้ำกัน เพราะมันอยู่ใน loop ไงล่ะ ทำให้ log ของ inner
ครั้งแรกก็ได้ log ต่อมาติดไปด้วย
วิธีแก้มีหลายแบบ หนึ่งในนั้นคือใช้ UUID
เพื่อสุ่มค่า UUID (บรรทัดที่ 27) ไว้ต่อท้ายชื่อ log ในแต่ละครั่งที่เรียก inner
เท่านี้ก็เรียบร้อยแล้วล่ะฮะ

ตัวอย่างในบล็อกนี้อยู่ใน repo ข้างล่างนี้นะฮะ