ส่วนใหญ่คนจะตระหนักถึงความจำเป็นของ log ก็ต่อเมื่อพวกเขาต้องการมันมากที่สุด แต่เมื่อแอปพลิเคชันของคุณล้มเหลว คำร้องเรียนของผู้ใช้เริ่ม湧ขึ้น และคุณไม่มีเบาะแสว่าจะแก้ไขอย่างไร มันก็ช้ามากแล้วที่จะเพิ่มข้อความ log บางอย่างที่อาจช่วยได้
Log ที่ดีคุ้มค่ามากกว่าคุณคิด 10 เท่า มันทำให้การวินิจฉัยบั๊กที่棘手กลายเป็นเรื่องง่าย และหากคุณทำ log ให้ถูกต้อง มันสามารถแจ้งเตือนคุณเกี่ยวกับปัญหาได้ก่อนที่ผู้ใช้จะสังเกตเห็น แต่มันหมายถึงอะไรที่จะ 'ทำ log ให้ถูกต้อง'?
การเริ่มต้น log ง่าย แต่การเชี่ยวชาญมันยาก ในโพสต์นี้ เราจะเจาะลึกว่าคุณสามารถใช้ log ของแอปพลิเคชัน Rails ให้เกิดประโยชน์สูงสุดได้อย่างไร
การบันทึก Log ใน Rails
มาพูดถึงพื้นฐานก่อน เมื่อคุณเริ่มแอปพลิเคชัน Rails ใหม่ การบันทึก log ถูกตั้งค่าให้คุณแล้ว Rails จะสร้างอินสแตนซ์ใหม่ของ ActiveSupport::Logger ที่คุณสามารถใช้ได้ทุกที่ภายในแอปพลิเคชันของคุณ
Rails logger เขียนไปที่ standard output หรือ log/<environment>.log และจะบันทึกคำขอที่เข้ามาหรือคำสั่งที่ execute อัตโนมัติ นอกจากข้อความ log ที่คุณเขียนไว้เอง
มีวิธีการกำหนดค่า Rails logs หลายวิธี ตามที่ระบุในเอกสารของ Rails อย่างดีเยี่ยม
เพื่อใช้ประโยชน์สูงสุดจาก log ของเรา เราสนใจโดยเฉพาะในการตั้งค่า log levels และ log formatting
แต่ก่อนจะโจมตีนั้น มาพูดถึงว่าทำไมเราควรเขียน log
Log ที่ดี Log ที่ไม่ดี
วัตถุประสงค์ของ log คือแจ้งให้คุณทราบเกี่ยวกับเหตุการณ์ของระบบ เพื่อให้คุณสามารถตอบสนองได้ ตัวอย่างเช่น เมื่อเกิดข้อผิดพลาด ข้อความ log ควรบอกคุณเกี่ยวกับมันในลักษณะที่คุณเข้าใจได้
ว่าคุณเข้าใจข้อความ log ได้ดีแค่ไหน ขึ้นอยู่กับว่ามันอธิบายได้ชัดเจนและมีบริบท (contextual) มากแค่ไหน ข้อความ log ที่อธิบายได้ชัดให้ข้อมูลที่เกี่ยวข้องเกี่ยวกับสิ่งที่เกิดขึ้น ข้อความ log ที่มีบริทุกต์รวมข้อมูลเกี่ยวกับสถานะของระบบเมื่อมันถูกเขียน
ลองพิจารณาตัวอย่างง่ายๆ เพื่อดูว่าทำไมเราต้องการทั้งสองอย่าง นี่คือโค้ดที่เรียก API ภายนอกและคืนค่าการตอบสนองเมื่อผู้ใช้ทำคำขอ
ตอนนี้ลองจินตนาการสถานการณ์ที่ลูกค้ารายงานปัญหา และคุณดูใน log เพื่อช่วยเหลือ นี่คือสิ่งที่คุณอาจเห็น:
ข้อความ log นี้ให้ข้อมูลบางส่วน แต่ไม่เพียงพอเลย เรายังไม่เข้าใจว่าสิ่งใดเป็นสาเหตุของข้อผิดพลาดที่ลูกค้ารายงาน ข้อความ log นี้ขาดความชัดเจนและบริบท มันคือ noise เราจะปรับปรุงได้อย่างไร?
Log Levels ใน Rails
Rails logger เริ่มต้นให้ log levels คือ DEBUG, INFO, WARN, และ ERROR การใช้พวกเขาช่วยให้คุณจัดกลุ่มข้อความ log เป็นหมวดหมู่ความเกี่ยวข้องต่างๆ ซึ่งไม่เพียงช่วยให้คุณกรองข้อความ log ได้ แต่ยังให้บริบทบางส่วน
การตัดสินใจว่าเมื่อไหร่ควรใช้ log level ไหนอาจท้าทาย นี่คือกฎอ้างอิงบางอย่าง:
DEBUG: ใช้ log level นี้สำหรับข้อมูลรายละเอียดเกี่ยวกับการดำเนินการภายในระบบ คุณอาจใช้ debug statements เมื่อเมธอดถูกเข้าหรือออก หรือที่ใดก็ตามที่คุณคิดว่าอาจเพิ่มคุณค่าในการดีบั๊กINFO: ทุกครั้งที่ระบบของคุณเปลี่ยนสถานะ หรือมีเหตุการณ์ที่เกี่ยวข้องเกิดขึ้น ให้ใช้ระดับINFOตัวอย่างข้อความ info ทั่วไปในบริบทของแอป Rails คือ คำขอที่รับเข้ามา คำขอที่ส่งไป API ภายนอก หรือ job เริ่มและจบWARN: ใช้ log นี้เพื่อบ่งชี้ว่ามีบางอย่างที่ไม่คาดคิดเกิดขึ้น มันยังไม่ใช่ปัญหา (เพราะแอปของคุณจัดการได้) แต่หากเกิดขึ้นซ้ำอาจควรได้รับความสนใจERROR: ใช้ log level นี้เมื่อเกิดข้อผิดพลาด Errors เป็นสถานะแอปพลิเคชันที่ไม่ถูกต้องซึ่งคุณต้องแก้ไขโดยเร็วที่สุด
คุณสามารถเปลี่ยนประเภทข้อความ log ที่แอปของคุณเขียนได้โดยแก้ไข environment file ที่เกี่ยวข้อง ปกติ Rails จะทิ้งข้อความ DEBUG ใน production แต่คุณสามารถเปลี่ยนได้
ลองนำคำแนะนำ log-level ข้างมาไปใช้กับตัวอย่างโค้ดของเรา
ตอนนี้ขณะดีบั๊ก เราสามารถระบุสาเหตุรากของปัญหาได้โดยดูข้อความ WARN และ ERROR น่าเสียดาย log messages ของเรายังไม่ได้ชัดเจนขึ้น
ข้อความ Log ที่อธิบายได้ชัดเจน
ข้อความ log ที่อธิบายได้ชัดไม่ทิ้งพื้นที่ให้การตีความ พวกมันให้รายละเอียดที่จำเป็นเพื่อให้ผู้อ่านรู้ทันทีว่เกิดอะไรขึ้น
เมื่อคุณอ่านข้อความอย่าง 'An error occurred' คุณจะเหลือคำถามว่าข้อผิดพลาดคืออะไร คุณต้องการหลีกเลี่ยงความสับสนนี้เมื่อเขียน log messages ของคุณ สิ่งสำคัญที่สุดเมื่อเขียน log messages ที่อธิบายได้ชัดคือการวางตัวเองในตำแหน่งผู้อ่าน log พวกเขาจะได้ข้อมูลทั้งหมดที่ต้องการเมื่ออ่าน log ของคุณไหม?
ลองเปลี่ยนข้อความ 'An error occurred' ให้เป็นข้อผิดพลาดเอง รวมถึงข้อความของมัน
นั่นคือการปรับปรุง ลองตรวจสอบ log messages อื่นๆ ในตัวอย่างของเรา 'Method entered' ไม่บอกว่าเมธอดไหนถูกเรียก 'Response success' ไม่รวมการตอบสนองจริง และ 'Response failure' ไม่ให้ข้อมูลเกี่ยวกับลักษณะของความล้มเหลว ลองเปลี่ยนกัน
หมายเหตุ: คุณอาจสังเกตว่าเราใช้ block syntax เมื่อทำ string interpolation กับ log ของเรา การใช้มันหลีกเลี่ยงการคำนวณที่ไม่จำเป็นเมื่อ log level ของแอปพลิเคชันสูงกว่า log level ของข้อความ ตัวอย่างเช่น log.debug("Some #{concatenation}") จะทำ string concatenation เสมอ แต่ log.debug { "Some #{concatenation}" } จะทำได้ก็ต่อเมื่อ log level ตั้งเป็น debug
log ของเราอ่านง่ายขึ้นมาก ตอนนี้มีข้อสงสัยน้อยมากเกี่ยวกับว่าแต่ละ log message หมายถึงอะไร
บริบทเพิ่มเติมสำหรับ Log Messages
log messages ใหม่ของเราให้ภาพชัดเจนของสิ่งที่เกิดขึ้น เนื่องจากเป็นตัวอย่างง่าย เราจึงเข้าใจดีว่าทำไมบางสิ่งถึงเกิดขึ้น ในความเป็นจริง มักจะไม่ง่ายอย่างนั้น
การให้ข้อมูลเพิ่มเติมเกี่ยวกับบริบทที่ระบบเขียนแต่ละข้อความสามารถช่วยให้ดีบั๊กง่ายขึ้นมาก
ตัวอย่างเช่น เมื่อบันทึกคำขอหรือการตอบสนอง อาจช่วยได้หากทราบว่าใครทำคำขอ การแนบ stack trace สำหรับข้อผิดพลาดอาจมีประโยชน์ เพื่อให้เราสามารถรวบรวมข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุของข้อผิดพลาด
หมายเหตุ: คุณน่าจะสังเกตว่าการสร้าง log messages พร้อมข้อมูลบริบทอาจลำบากเมื่อคุณใช้ logger เริ่มต้นของ Rails โชคดีที่ custom loggers เช่น Ougai หรือ MrLogaLoga ทำให้เรื่องนี้ง่ายขึ้นมาก
Structured Logging
เมื่อคุณทำให้ log อ่านง่ายสำหรับมนุษย์แล้ว คุณพร้อมที่จะนำไปสู่ระดับถัดไป คุณตอนนี้ต้องทำให้ log อ่านได้โดยเครื่องจักร เพื่อให้คุณสามารถใช้มันสำหรับการตรวจสอบและวิเคราะห์ข้อมูล
รูปแบบที่ฉันชอบคือ JSON แต่ขึ้นอยู่กับเครื่องมือที่คุณใช้ คุณอาจชอบรูปแบบ log อื่นๆ เช่น Logstash คุณสามารถเปลี่ยนรูปแบบข้อความ log ได้โดยการสร้าง custom log formatter
แทนที่จะเขียน custom formatters เอง คุณสามารถใช้ gem หลายตัวที่ให้ custom log formatters ฉันแนะนำ Lograge ซึ่งไม่เพียงแต่ให้ formatting พร้อมใช้งาน แต่ยังทำให้ request formatting ของ Rails ที่ค่อนข้าง verbose สะอาดขึ้นอ่านง่ายขึ้น
การบันทึก Log ใน Rails ด้วย AppSignal
ตอนนี้ log ของคุณอ่านง่ายแล้ว จะดีมากถ้าทำให้เข้าถึงได้ง่ายขึ้น เพราะคุณไม่อยาก SSH เข้าเครื่องแล้ว tail หรือ grep log ที่นั่นใช่ไหม?
โชคดีที่ AppSignal เพิ่งเปิดตัวฟีเจอร์ logging แบบ beta! สิ่งนี้ช่วยให้คุณตรวจสอบและวิเคราะห์ Rails logs ได้โดยตรงในเว็บอินเทอร์เฟซของ AppSignal
คุณสามารถเข้าถึง logging beta ของเราผ่านแท็บ 'Logging' ในเมนูด้านซ้าย:

หากคุณปฏิบัติตามขั้นตอนในเอกสาร Ruby logging ของเรา คุณสามารถกำหนดค่า Rails Logger ให้ใช้คลาส AppSignal::Logger โดยวางโค้ดด้านล่างในไฟล์ config/environment.rb ก่อน initialization:
คุณยังสามารถ ingest structured logs ในรูปแบบ JSON หรือด้วย Lograge ได้
เมื่อคุณตั้งค่าทุกอย่างเสร็จ คุณควรจะเห็น Rails logs ของคุณปรากฏบน AppSignal!

การบันทึก Log และการรายงานข้อผิดพลาด
คุณกำลังอ่านนี้บนบล็อกของ AppSignal ดังนั้นคุณอาจสงสัยว่า: ทำไมต้องกังวลเกี่ยวกับ logging หากฉันใช้แพลตฟอร์มรายงานข้อผิดพลาดที่ดีอยู่แล้ว?
คำถามที่ดี เครื่องมือติดตามข้อผิดพลาดให้ข้อมูลรายละเอียดเกี่ยวกับข้อผิดพลาดของแอปพลิเคชัน พวกเขาจับ stack trace เต็มของข้อผิดพลาดแอปพลิเคชันใดๆ และให้ข้อมูลบริบทมากมายทันที
อย่างไรก็ตาม แม้เครื่องมือส่วนใหญ่จะอนุญาตให้จับเหตุการณ์อื่นๆ นอกจากข้อผิดพลาด การส่งข้อความจำนวนมากแบบสุ่มให้เครื่องมือเหล่านี้โดยทั่วไปเป็นไปไม่ได้ คุณจะไม่สามารถแทนที่ข้อมูลรายละเอียดเชิงประวัติศาสตร์ที่ log ที่เขียนดีให้มา
ในความเป็นจริง log ที่เขียนดีจะเสริมและสนับสนุนเครื่องมือติดตามข้อผิดพลาด คุณควรใช้ทั้งสองอย่าง
สรุป
ในโพสต์นี้ เราได้ดูว่าคุณสามารถใช้ประโยชน์สูงสุดจาก log ของคุณได้อย่างไร เราเห็นว่าการเริ่มต้น logging ใน Rails ง่าย แต่การเขียน log ที่มีประโยชน์อาจท้าทาย
Log ที่ขาดความชัดเจนหรือบริบทจะแค่เต็มดิสก์ของคุณในขณะที่ให้คุณค่าน้อย อย่างไรก็ตาม log ที่ใช้ log levels ถูกต้องและให้ผู้อ่านข้อมูลที่พวกเขาต้องการเป็นทรัพย์สินที่ยอดเยี่ยม
เมื่อคุณเชี่ยวชาญการเขียน log messages ที่ยอดเยี่ยมแล้ว คุณสามารถนำไปต่อได้โดยจัดรูปแบบให้เหมาะกับการวิเคราะห์ log และการกรองง่าย
เรายังสำรวจวิธีใช้ฟีเจอร์ logging ใหม่ของ AppSignal เพื่อเข้าถึง log ของคุณได้ง่าย สุดท้ายเราก็ 언급ถึงว่า log ควรเสริมเครื่องมือติดตามข้อผิดพลาด ไม่ใช่แทนที่พวกเขา
ขอให้บันทึก log สนุกสนาน!
P.S. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่เผยแพร่ สมัครรับจดหมายข่าว Ruby Magic และจะไม่พลาดโพสต์ใดๆ!

Hans-Jörg Schnedlitz
ผู้เขียน אורח Hans เป็นวิศวกร Rails จากเวียนนา ออสเตรีย เขาใช้เวลาส่วนใหญ่เขียนโค้ดหรืออ่านเกี่ยวกับโค้ด และบางครั้งเขียนเกี่ยวกับมันบนบล็อกของเขา! เมื่อเขาไม่ได้นั่งหน้าจอ คุณน่าจะพบเขานอกบ้าน ปีนเขาบางลูก
บทความทั้งหมดโดย Hans-Jörg Schnedlitz