เชื่อได้ว่า programmer ทุกคนใช้ print เป็นตัว debug เช็คค่าตัวแปรนั่นนี่ (ผมก็ทำ) แต่เรามีวิธีที่ดีกว่านั้น

Logging module เป็น module ที่มาพร้อม Python เลย ไม่ต้องลงเพิ่ม มีความสามารถครบเครื่องเรื่องการ log ต่างๆ ให้เรากลับไปเช็คได้ทีหลัง ไม่ต้องมาดู print อะไรออกมา ไถๆ ย้อนกลับไปดูหลายบรรทัด และมันยังจัดการบันทึก log ลง file ได้อย่างมีประสิทธิภาพด้วยนะฮะ

นี่เลยเป็นเหตุผลที่ บล็อกนี้เรามาคุยกันเรื่องเจ้า Logging module ฮะ

แล้วถ้าใครอยากอ่าน official doc ก็ไปตามลิงก์ด้านล่างนี้ได้นะฮะ แต่สำหรับบล็อกนี้ เราจะเน้นกันที่การใช้งานทั่วไปกันก่อน

logging — Logging facility for Python
Source code: Lib/logging/__init__.py Important: This page contains the API reference information. For tutorial information and discussion of more advanced topics, see Basic Tutorial, Advanced Tutor…

เริ่มด้วยท่ามาตรฐาน

เราจะเริ่มจาก 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 ข้างล่างนี้นะฮะ

GitHub - bluebirz/sample-python-logging
Contribute to bluebirz/sample-python-logging development by creating an account on GitHub.