หากคุณต้องการทำสิ่งต่าง ๆ ให้ดีคุณต้องลับเครื่องมือของคุณก่อน
โปรแกรมเมอร์หลายคนอาจลืมว่าการบันทึกพฤติกรรมของแอปพลิเคชันมีความสำคัญเพียงใด เมื่อพบข้อบกพร่องพร้อมกันที่เกิดจากแรงดันสูงในสภาพแวดล้อมแบบมัลติเธรดคุณสามารถเข้าใจความสำคัญของบันทึกการบันทึก
บางคนมีความสุขมากที่จะเพิ่มประโยคนี้ลงในรหัส:
log.info ("มีความสุขและไร้ความกังวลในการบันทึก");
เขาอาจไม่ได้ตระหนักถึงความสำคัญของบันทึกแอปพลิเคชันในการบำรุงรักษาการปรับแต่งและการระบุข้อผิดพลาด ฉันคิดว่า SLF4J เป็น API การบันทึกที่ดีที่สุดส่วนใหญ่เป็นเพราะมันรองรับวิธีที่ดีในการฉีดสคีมา:
log.debug ("พบ {} บันทึกการจับคู่ตัวกรอง: '{}'", บันทึก, ตัวกรอง);
สำหรับ log4j คุณทำได้เท่านั้น:
log.debug ("พบ" + Records + "ตัวกรอง RecordSmatching: '" + ตัวกรอง + "'");
การเขียนนี้ไม่เพียง แต่การอ่านที่มีคำพูดและไม่ดีเท่านั้น แต่ยังมีการประกบสตริงที่มีผลต่อประสิทธิภาพ (เมื่อระดับนี้ไม่ต้องการผลลัพธ์)
SLF4J แนะนำคุณลักษณะการฉีด {} และเนื่องจากการหลีกเลี่ยงการประกบสตริงทุกครั้งวิธีการ toString จะไม่ถูกเรียกและไม่จำเป็นต้องเพิ่ม isdebugenabled SLF4J เป็นแอพพลิเคชั่นของโหมดปรากฏตัวมันเป็นเพียงด้านหน้า สำหรับการใช้งานเฉพาะฉันขอแนะนำกรอบการเข้าสู่ระบบ ฉันเคยโฆษณามาก่อนแล้วแทนที่จะเป็น log4j ที่สมบูรณ์แล้ว มันมีคุณสมบัติที่น่าสนใจมากมาย ซึ่งแตกต่างจาก log4j มันยังอยู่ระหว่างการพัฒนาและการปรับปรุงที่ใช้งานอยู่
เครื่องมืออีกอย่างที่แนะนำคือ Perf4J:
perf4j คือ System.currentTimeMillis () เนื่องจาก log4j คือ System.out.println ()
เช่นเดียวกับ log4j เป็นทางเลือกที่ดีกว่าสำหรับ system.out.println, perf4j เป็นเหมือนการแทนที่ system.currenttimemillis () ฉันได้แนะนำ Perf4J ในโครงการและสังเกตว่ามันทำงานได้อย่างไรภายใต้โหลดสูง ผู้ดูแลระบบและผู้ใช้ทางธุรกิจต่างก็ตกตะลึงด้วยแผนภูมิที่สวยงามที่จัดทำโดยอุปกรณ์นี้ เราสามารถดูปัญหาประสิทธิภาพได้ตลอดเวลา perf4j ควรเป็นบทความพิเศษที่จะพูดคุยเกี่ยวกับ ตอนนี้คุณสามารถดูคู่มือนักพัฒนาได้ก่อน นอกจากนี้ยังมี Ceki Gülcü (ผู้สร้างโครงการ Log4j, SLF4J และ Logback) ที่ให้วิธีง่ายๆสำหรับเราในการลบการพึ่งพาการติดบัญชีคอมมอนส์
อย่าลืมระดับบันทึก
ทุกครั้งที่คุณต้องการเพิ่มบรรทัดของบันทึกคุณจะคิดว่าควรใช้ระดับบันทึกใดที่นี่? ประมาณ 90% ของโปรแกรมเมอร์ไม่ได้ให้ความสนใจกับปัญหานี้มากนัก พวกเขาใช้ระดับหนึ่งในการบันทึกบันทึกโดยปกติจะเป็นข้อมูลหรือการดีบัก ทำไม
Framework Log มีข้อได้เปรียบที่สำคัญสองประการเมื่อเทียบกับ System.out: การจำแนกประเภทและระดับ ทั้งสองอนุญาตให้คุณเลือกกรองบันทึกอย่างถาวรหรือเพียงแค่เมื่อแก้ไขข้อผิดพลาด
ข้อผิดพลาด: เกิดข้อผิดพลาดร้ายแรงและต้องจัดการทันที ข้อผิดพลาดระดับนี้ไม่สามารถใช้งานได้กับระบบใด ๆ ตัวอย่างเช่น: ข้อยกเว้นตัวชี้ NULL ฐานข้อมูลไม่พร้อมใช้งานและกรณีการใช้งานของเส้นทางวิกฤตไม่สามารถดำเนินการต่อไปได้
เตือน: กระบวนการที่ตามมาจะดำเนินต่อไป แต่ควรดำเนินการอย่างจริงจัง ที่จริงแล้วฉันหวังว่าจะมีสองระดับที่นี่: หนึ่งคือปัญหาที่ชัดเจนของการแก้ปัญหา (เช่น "ข้อมูลปัจจุบันไม่พร้อมใช้งานโดยใช้ข้อมูลแคช") และอีกข้อมูลหนึ่งคือปัญหาที่อาจเกิดขึ้นและคำแนะนำ (เช่น "โปรแกรมทำงานในโหมดการพัฒนา" หรือ "รหัสผ่านของคอนโซลการจัดการไม่ปลอดภัยเพียงพอ"
DEBUG: สิ่งที่นักพัฒนามีความกังวล หลังจากนั้นฉันจะพูดถึงสิ่งที่ควรบันทึกในระดับนี้
Trace: ข้อมูลรายละเอียดเพิ่มเติมที่ใช้ในขั้นตอนการพัฒนาเท่านั้น คุณอาจต้องให้ความสนใจกับข้อมูลนี้ภายในระยะเวลาอันสั้นหลังจากเปิดตัวผลิตภัณฑ์ แต่บันทึกบันทึกเหล่านี้เป็นเพียงชั่วคราวและควรปิดในที่สุด เป็นการยากที่จะแยกแยะความแตกต่างระหว่างการดีบักและการติดตาม แต่ถ้าคุณเพิ่มบรรทัดของบันทึกและลบหลังจากการพัฒนาและการทดสอบบันทึกควรอยู่ในระดับการติดตาม
รายการด้านบนเป็นเพียงข้อเสนอแนะคุณสามารถบันทึกตามกฎของคุณเอง แต่ควรมีกฎบางอย่างที่ดีที่สุด ประสบการณ์ส่วนตัวของฉันคือ: อย่ากรองบันทึกที่ระดับรหัส แต่ใช้ระดับบันทึกที่ถูกต้องเพื่อกรองข้อมูลที่ต้องการอย่างรวดเร็วซึ่งสามารถประหยัดเวลาได้มาก
สิ่งสุดท้ายที่จะพูดคือสิ่งที่น่าอับอายนี้คือ*คำสั่งแบบมีเงื่อนไขที่เปิดใช้งาน* บางคนชอบเพิ่มสิ่งนี้ก่อนแต่ละบันทึก:
if (log.isdebugenabled ()) log.debug ("สถานที่สำหรับโฆษณาของคุณ");โดยส่วนตัวแล้วฉันคิดว่าคุณควรหลีกเลี่ยงการเพิ่มสิ่งที่ยุ่งเหยิงนี้ลงในรหัส การแสดงดูเหมือนจะไม่ได้รับการปรับปรุงมากนัก (โดยเฉพาะหลังจากใช้ SLF4J) ซึ่งเป็นเหมือนการเพิ่มประสิทธิภาพก่อนวัยอันควร นอกจากนี้คุณไม่พบว่ามันซ้ำซ้อนหรือไม่? ไม่ค่อยมีคำสั่งการตัดสินที่ชัดเจนนี้อย่างชัดเจนเว้นแต่เราจะพิสูจน์ว่าการสร้างข้อความบันทึกนั้นมีราคาแพงเกินไป มิฉะนั้นคุณสามารถจำได้มากที่สุดเท่าที่ควรและปล่อยให้กรอบการทำงานของบันทึกกังวลเกี่ยวกับเรื่องนี้
คุณรู้หรือไม่ว่าคุณกำลังบันทึกอะไร?
ทุกครั้งที่คุณเขียนบรรทัดของบันทึกใช้เวลาสักครู่เพื่อดูว่าคุณกำลังพิมพ์อะไรในไฟล์บันทึก อ่านบันทึกของคุณและค้นหาว่าข้อยกเว้นอยู่ที่ไหน ก่อนอื่นอย่างน้อยหลีกเลี่ยงข้อยกเว้นตัวชี้ว่างเปล่า:
log.debug ("คำขอการประมวลผลด้วย id: {}", request.getId ());
คุณได้รับการยืนยันแล้วว่าคำขอไม่เป็นโมฆะ?
คอลเลกชันการบันทึกยังเป็นหลุมขนาดใหญ่ หากคุณใช้ไฮเบอร์เนตเพื่อรับคอลเลกชันของวัตถุโดเมนจากฐานข้อมูลคุณเขียนโดยไม่ตั้งใจเช่นนี้: log.debug ("ผู้ใช้ที่ส่งคืน: {}" ผู้ใช้);
SLF4J จะเรียกวิธีการ ToString เมื่อพิมพ์คำสั่งนี้แน่นอนว่านี่เป็นเรื่องเจ๋ง อย่างไรก็ตามหากหน่วยความจำล้นปัญหาการเลือก N+1, เธรดดาราดาร์สู่ความตาย, ข้อยกเว้นการเริ่มต้นล่าช้า, พื้นที่เก็บข้อมูลบันทึกหมดลง ... สิ่งเหล่านี้อาจเกิดขึ้นได้ วิธีที่ดีที่สุดคือการบันทึกเฉพาะ ID ของวัตถุ (หรือเฉพาะขนาดของคอลเลกชัน) อย่างไรก็ตามการรวบรวมรหัสต้องเรียกใช้วิธีการ GetID สำหรับแต่ละวัตถุซึ่งไม่ใช่เรื่องง่ายใน Java Groovy มีตัวดำเนินการขยายตัวที่ยอดเยี่ยม (ผู้ใช้*.ID) ใน Java เราสามารถใช้ห้องสมุด Pommons Beanutils เพื่อจำลอง:
log.debug ("การส่งคืนรหัสผู้ใช้: {}", รวบรวม (ผู้ใช้, "id"));
นี่อาจเป็นวิธีการรวบรวมวิธีการ:
คอลเลกชันสาธารณะแบบคงที่ (คอลเลกชันคอลเลกชันสตริง PropertyName) {return collectionUtEtils.Collect (คอลเลกชันใหม่ beantopropertyvaluetransformer (PropertyName));}ในที่สุดวิธีการ toString อาจไม่ถูกนำไปใช้อย่างถูกต้องหรือใช้
ก่อนอื่นเพื่อบันทึกมีหลายวิธีในการสร้าง toString สำหรับแต่ละคลาส เป็นการดีที่สุดที่จะใช้ ToStringBuilder เพื่อสร้าง (แต่ไม่ใช่รุ่นของการใช้งานการสะท้อนกลับ)
ประการที่สองให้ความสนใจกับอาร์เรย์และชุดผิดปกติ การใช้งานของอาร์เรย์และคอลเลกชันทางเลือกบางอย่างอาจไม่เรียกวิธีการ toString ของแต่ละองค์ประกอบทีละหนึ่ง สามารถใช้วิธีการอาร์เรย์#deeptoString โดย JDK ตรวจสอบบันทึกที่คุณพิมพ์ด้วยตัวเองเพื่อดูว่ามีข้อมูลใด ๆ เกี่ยวกับข้อยกเว้นรูปแบบหรือไม่
หลีกเลี่ยงผลข้างเคียง
การพิมพ์บันทึกโดยทั่วไปไม่ได้ส่งผลกระทบต่อประสิทธิภาพของโปรแกรมมากนัก เมื่อเร็ว ๆ นี้เพื่อนของฉันได้โยนข้อยกเว้น Hibernate LazyInitializationException บนระบบที่ทำงานบนแพลตฟอร์มพิเศษบางอย่าง อย่างที่คุณอาจเดาได้จากสิ่งนี้บันทึกบางอย่างที่ส่งผลให้เกิดการเริ่มต้นคอลเลกชันเริ่มต้นล่าช้าเมื่อมีการเชื่อมต่อเซสชัน ในกรณีนี้หากระดับการบันทึกเพิ่มขึ้นคอลเลกชันจะไม่เริ่มต้นอีกต่อไป หากคุณไม่ทราบข้อมูลบริบทนี้คุณจะต้องใช้เวลานานแค่ไหนในการค้นพบข้อผิดพลาดนี้
ผลข้างเคียงอีกประการหนึ่งคือมันมีผลต่อความเร็วในการทำงานของโปรแกรม คำตอบอย่างรวดเร็วสำหรับคำถามนี้: หากบันทึกถูกพิมพ์มากเกินไปหรือการประกบ toString และสตริงไม่ถูกใช้อย่างถูกต้องการพิมพ์บันทึกจะมีผลกระทบเชิงลบต่อประสิทธิภาพ มันใหญ่แค่ไหน? ฉันเคยเห็นโปรแกรมรีสตาร์ททุก ๆ 15 นาทีเพราะบันทึกมากเกินไปทำให้เธรดอดตาย นี่คือผลข้างเคียง! จากประสบการณ์ของฉันการพิมพ์หนึ่งร้อยเมกะไบต์ในหนึ่งชั่วโมงเกือบจะเป็นขีด จำกัด สูงสุด
แน่นอนว่าหากกระบวนการทางธุรกิจถูกยกเลิกเนื่องจากข้อยกเว้นการพิมพ์บันทึกผลข้างเคียงนี้จะดีมาก ฉันมักจะเห็นคนเขียนสิ่งนี้เพื่อหลีกเลี่ยงสิ่งนี้:
ลอง {log.trace ("id =" + request.getUser (). getId () + "เข้าถึง" + manager.getPage (). getUrl (). toString ())} catch (nullpointerexception e) {}นี่เป็นรหัสที่แท้จริง แต่เพื่อให้โลกบริสุทธิ์ยิ่งขึ้นโปรดอย่าเขียนแบบนี้
อธิบายอย่างชัดเจน
บันทึกบันทึกแต่ละรายการมีข้อมูลและคำอธิบาย ดูตัวอย่างนี้:
log.debug ("การประมวลผลข้อความ"); log.debug (message.getjmsmessageId ()); log.debug ("ข้อความที่มี id '{}' ประมวลผล", message.getJmsageId ()); เมื่อแก้ไขข้อผิดพลาดในระบบที่ไม่คุ้นเคยคุณชอบบันทึกแบบไหน? เชื่อใจฉันตัวอย่างเหล่านี้เป็นเรื่องธรรมดา นอกจากนี้ยังมีโหมดลบ:
if (ข้อความอินสแตนซ์ของ TextMessage)
-
อื่น
log.warn ("ประเภทข้อความที่ไม่รู้จัก");
เป็นการยากที่จะเพิ่มประเภทข้อความรหัสข้อความ ฯลฯ ลงในบันทึกคำเตือนนี้หรือไม่? ฉันรู้ว่าเกิดข้อผิดพลาด แต่มันคืออะไร? ข้อมูลบริบทคืออะไร?
ตัวอย่างเชิงลบที่สามคือ "Magic Log" ตัวอย่างจริง: โปรแกรมเมอร์จำนวนมากในทีมรู้ว่า 3 ≥ตัวเลขติดตาม! หมายเลขตามด้วยหมายเลข # ตามด้วยบันทึกหมายเลขหลอกหมายถึง "ได้รับข้อความที่มี ID XYZ" ไม่มีใครอยากเปลี่ยนบันทึกนี้ หากมีคนพิมพ์คีย์บอร์ดและเลือกสตริง "&&&!#" ที่ไม่ซ้ำกันเขาจะพบข้อมูลที่เขาต้องการอย่างรวดเร็ว
ผลลัพธ์คือไฟล์บันทึกทั้งหมดดูเหมือนสตริงตัวอักษรขนาดใหญ่แบบสุ่ม บางคนไม่สามารถช่วยได้ แต่สงสัยว่านี่เป็นโปรแกรม Perl หรือไม่
ไฟล์บันทึกควรอ่านได้ชัดเจนและอธิบายตัวเอง อย่าใช้หมายเลขเวทย์มนตร์ค่าบันทึกตัวเลขรหัสและบริบทของพวกเขา บันทึกข้อมูลที่ประมวลผลและความหมาย บันทึกสิ่งที่โปรแกรมกำลังทำ บันทึกที่ดีควรเป็นเอกสารที่ดีของรหัสโปรแกรม
ฉันเคยพูดถึงการพิมพ์รหัสผ่านและมีข้อมูลส่วนบุคคลหรือไม่? ฉันเชื่อว่าไม่มีโปรแกรมเมอร์โง่ ๆ
ปรับรูปแบบของคุณ
รูปแบบการบันทึกเป็นเครื่องมือที่มีประโยชน์มากซึ่งจะเพิ่มข้อมูลบริบทที่มีค่าลงในบันทึก แต่คุณควรคิดอย่างชัดเจนเกี่ยวกับข้อมูลประเภทใดที่รวมอยู่ในรูปแบบของคุณ ตัวอย่างเช่นมันไม่มีความหมายที่จะบันทึกวันที่ในบันทึกที่เขียนทุกรอบชั่วโมงเนื่องจากชื่อบันทึกของคุณมีข้อมูลนี้อยู่แล้ว ในทางตรงกันข้ามหากคุณไม่ได้บันทึกชื่อเธรดเมื่อสองเธรดทำงานแบบขนานคุณจะไม่สามารถติดตามเธรดผ่านบันทึก - บันทึกได้ทับกันเข้าด้วยกัน ในแอปพลิเคชั่นเดียวเธรดมันก็โอเคที่จะทำสิ่งนี้ แต่นั่นก็เป็นอดีตที่ผ่านมา
จากประสบการณ์ของฉันรูปแบบบันทึกในอุดมคติควรรวม (ยกเว้นข้อมูลบันทึกของตัวเองแน่นอน): เวลาปัจจุบัน (ไม่มีวันที่ความแม่นยำมิลลิวินาที) ระดับบันทึกชื่อเธรดชื่อบันทึกง่าย ๆ (ไม่ใช่ชื่อเต็ม) และข้อความ ใน logback มันจะมีลักษณะเช่นนี้:
<appender name = "stdout"> <encoder> <matter>%d {hh: mm: ss.sss}%-5level [%เธรด] [%logger {0}]%m%n </รูปแบบ> </coder> </appender>ชื่อไฟล์ชื่อคลาสหมายเลขบรรทัดไม่จำเป็นต้องอยู่ในรายการแม้ว่าพวกเขาจะดูมีประโยชน์ ฉันเคยเห็นการบันทึกที่ว่างเปล่าในรหัส:
log.info (""); เนื่องจากโปรแกรมเมอร์เชื่อว่าหมายเลขบรรทัดจะเป็นส่วนหนึ่งของรูปแบบบันทึกและเขารู้ว่าหากข้อความบันทึกที่ว่างเปล่าปรากฏบนบรรทัด 67 ของไฟล์นั่นหมายความว่าผู้ใช้ได้รับการรับรองความถูกต้องแล้ว ไม่เพียงแค่นั้นบันทึกชื่อชื่อคลาสหรือหมายเลขบรรทัดมีผลกระทบอย่างมากต่อประสิทธิภาพ
คุณลักษณะขั้นสูงของเฟรมเวิร์กการบันทึกคือบริบทการวินิจฉัยที่แมป MDC เป็นเพียงแผนที่ที่มีเธรดท้องถิ่น คุณสามารถใส่คู่คีย์-ค่าใด ๆ ลงในแผนที่นี้เพื่อให้บันทึกบันทึกทั้งหมดของเธรดนี้สามารถรับข้อมูลที่เกี่ยวข้องจากแผนที่นี้เป็นส่วนหนึ่งของรูปแบบผลลัพธ์
บันทึกพารามิเตอร์และค่าส่งคืนของวิธีการ
หากคุณพบข้อผิดพลาดในขั้นตอนการพัฒนาคุณมักจะใช้ดีบักเกอร์เพื่อติดตามเหตุผลเฉพาะ ตอนนี้สมมติว่าคุณจะไม่ใช้ดีบักเกอร์อีกต่อไป ตัวอย่างเช่นเนื่องจากข้อบกพร่องนี้ปรากฏในสภาพแวดล้อมของผู้ใช้เมื่อไม่กี่วันที่ผ่านมาสิ่งที่คุณได้รับคือบันทึกไม่กี่รายการ คุณจะรู้อะไรได้บ้าง?
หากคุณทำตามหลักการง่ายๆของการพิมพ์พารามิเตอร์ในและนอกสำหรับแต่ละวิธีคุณไม่จำเป็นต้องมีการดีบักเกอร์เลย แน่นอนว่าแต่ละวิธีอาจเข้าถึงระบบภายนอกบล็อกรอ ฯลฯ และสิ่งเหล่านี้ควรนำมาพิจารณา เพียงอ้างถึงรูปแบบต่อไปนี้:
Public String PrintDocument (เอกสารเอกสารโหมดโหมด) {log.debug ("ป้อน PrintDocument (doc = {}, mode = {})", doc, mode); string id = ... ; // การดำเนินการพิมพ์ยาว log.debug ("ออกจาก printDocument (): {}", id); return id;}เนื่องจากคุณบันทึกบันทึกที่จุดเริ่มต้นและจุดสิ้นสุดของวิธีการคุณสามารถค้นหารหัสที่ไม่มีประสิทธิภาพด้วยตนเองและตรวจจับสาเหตุที่อาจทำให้เกิดการหยุดชะงักและความหิว - คุณเพียงแค่ต้องดูว่าไม่มี "การออก" หลังจาก "เข้าสู่" หากความหมายของชื่อวิธีการของคุณชัดเจนมันจะเป็นสิ่งที่น่าพอใจในการล้างบันทึก ในทำนองเดียวกันการวิเคราะห์ข้อยกเว้นนั้นง่ายกว่าเพราะคุณรู้ว่าคุณกำลังทำอะไรในแต่ละขั้นตอน หากมีหลายวิธีในการบันทึกในรหัสคุณสามารถใช้ส่วน AOP เพื่อทำให้เสร็จสมบูรณ์ สิ่งนี้จะช่วยลดรหัสที่ซ้ำกัน แต่คุณต้องระมัดระวังอย่างมากเมื่อใช้งานและหากคุณไม่ระวังมันอาจนำไปสู่เอาต์พุตบันทึกจำนวนมาก
ระดับที่เหมาะสมที่สุดสำหรับบันทึกประเภทนี้คือการดีบักและติดตาม หากคุณพบว่าวิธีการที่เรียกบ่อยเกินไปและบันทึกบันทึกของมันอาจส่งผลกระทบต่อประสิทธิภาพคุณเพียงแค่ต้องลดระดับการบันทึกหรือลบบันทึกโดยตรง (หรือเพียงหนึ่งในวิธีการเรียกทั้งหมด?) อย่างไรก็ตามบันทึกมากเกินไปจะดีกว่าน้อยกว่า คิดว่าการบันทึกเป็นการทดสอบหน่วยรหัสของคุณควรได้รับการคุ้มครองด้วยบันทึกเช่นเดียวกับการทดสอบหน่วยของมันมีอยู่ทุกที่ ไม่มีส่วนหนึ่งของระบบไม่จำเป็นต้องมีการบันทึกเลย โปรดจำไว้ว่าบางครั้งคุณจำเป็นต้องรู้ว่าระบบของคุณทำงานอย่างถูกต้องหรือไม่และคุณสามารถดูบันทึกที่ทำให้หน้าจอท่วมหน้าจอได้อย่างต่อเนื่องเท่านั้น
สังเกตระบบภายนอก
คำแนะนำนี้แตกต่างจากข้อก่อนหน้านี้เล็กน้อย: หากคุณกำลังสื่อสารกับระบบภายนอกอย่าลืมบันทึกข้อมูลขาออกและการอ่านข้อมูลของระบบของคุณ การรวมระบบเป็นงานที่น่าเบื่อและการวินิจฉัยปัญหาระหว่างสองแอปพลิเคชัน (ลองนึกภาพ บริษัท ต่าง ๆ สภาพแวดล้อมทีมงานด้านเทคนิค) เป็นเรื่องยากโดยเฉพาะ เมื่อเร็ว ๆ นี้เราพบว่าการบันทึกเนื้อหาข้อความที่สมบูรณ์รวมถึงส่วนหัวของ SOAP และ HTTP ของ Apache CXF นั้นมีประสิทธิภาพมากในระหว่างขั้นตอนการรวมและการทดสอบของระบบ
สิ่งนี้มีราคาแพงและหากมีผลต่อประสิทธิภาพคุณสามารถปิดบันทึกได้เท่านั้น แต่ด้วยวิธีนี้ระบบของคุณอาจทำงานได้อย่างรวดเร็วและแขวนอย่างรวดเร็วดังนั้นคุณจึงไม่สามารถทำอะไรเกี่ยวกับมันได้? เมื่อรวมเข้ากับระบบภายนอกคุณสามารถระมัดระวังเป็นพิเศษและเตรียมพร้อมที่จะเสียสละค่าใช้จ่ายบางอย่าง หากคุณโชคดีพอและการรวมระบบได้รับการจัดการโดย ESB มันเป็นการดีที่สุดที่จะบันทึกคำขอและการตอบสนองบนรถบัส คุณสามารถอ้างถึงองค์ประกอบบันทึกนี้ของ Mule
บางครั้งจำนวนข้อมูลที่แลกเปลี่ยนกับระบบภายนอกกำหนดว่าเป็นไปไม่ได้ที่คุณจะเขียนทุกอย่าง ในทางกลับกันมันเป็นการดีที่สุดที่จะเก็บทุกอย่างไว้ในบันทึกในระหว่างขั้นตอนการทดสอบและขั้นตอนการเปิดตัวก่อนเวลาและเตรียมพร้อมที่จะเสียสละประสิทธิภาพ สามารถทำได้โดยการปรับระดับบันทึก ลองดูเคล็ดลับต่อไปนี้:
คอลเลกชัน <teger> requestIds = // ... ถ้า (log.isdebugenabled ()) log.debug ("การประมวลผลรหัส: {}", requestIds); else log.info ("รหัสการประมวลผลขนาด: {}", requestIDSIZE (); หากตัวบันทึกนี้ได้รับการกำหนดค่าให้อยู่ในระดับการดีบักมันจะพิมพ์คอลเลกชันที่สมบูรณ์ของรหัสคำขอ หากมีการกำหนดค่าให้พิมพ์ข้อมูลข้อมูลจะส่งออกเฉพาะขนาดของชุดเท่านั้น คุณอาจถามฉันว่าฉันลืมเงื่อนไข ISINFOENABLED หรือไม่โปรดดูคำแนะนำที่สอง อีกสิ่งหนึ่งที่ควรสังเกตที่นี่คือชุดของ IDs ไม่สามารถเป็นโมฆะได้ แม้ว่ามันจะสามารถพิมพ์ตามปกติภายใต้การดีบักเป็น null มันเป็นตัวชี้โมฆะขนาดใหญ่เมื่อกำหนดค่าเป็นข้อมูล จำผลข้างเคียงที่กล่าวถึงในข้อเสนอแนะที่ 4 ได้หรือไม่?
แก้ไขข้อยกเว้นการบันทึก
ก่อนอื่นอย่าบันทึกข้อยกเว้นให้เฟรมเวิร์กหรือคอนเทนเนอร์ทำสิ่งนี้ แน่นอนว่ามีข้อยกเว้น: หากคุณโยนข้อยกเว้น (RMI, EJB ฯลฯ ) จากบริการระยะไกลข้อยกเว้นจะถูกจัดลำดับเพื่อให้แน่ใจว่าพวกเขาสามารถส่งคืนให้กับลูกค้า (ส่วนหนึ่งของ API) มิฉะนั้นลูกค้าจะได้รับ noclassDeffoundError หรือข้อยกเว้นแปลก ๆ อื่น ๆ แทนที่จะเป็นข้อความแสดงข้อผิดพลาดจริง
การบันทึกข้อยกเว้นเป็นหนึ่งในความรับผิดชอบที่สำคัญที่สุดของการบันทึก แต่โปรแกรมเมอร์จำนวนมากมักจะใช้การบันทึกเป็นวิธีในการจัดการข้อยกเว้น พวกเขามักจะส่งคืนค่าเริ่มต้น (โดยปกติแล้ว null, 0 หรือสตริงเปล่า) และแกล้งทำเป็นไม่เกิดขึ้น บางครั้งพวกเขาจะบันทึกข้อยกเว้นก่อนจากนั้นสรุปข้อยกเว้นแล้วโยนมันออกไป:
log.error ("ข้อยกเว้น io", e); โยน customexception ใหม่ (E);ด้วยวิธีนี้ข้อมูลสแต็กมักจะถูกพิมพ์สองครั้งเนื่องจากสถานที่ที่มีการจับข้อยกเว้น mycustomexception จะถูกพิมพ์อีกครั้ง บันทึกบันทึกหรือห่อมันแล้วโยนออกอย่าใช้ในเวลาเดียวกันมิฉะนั้นบันทึกของคุณจะดูสับสน
ถ้าเราต้องการบันทึกจริงๆ ด้วยเหตุผลบางอย่าง (สันนิษฐานว่าไม่ได้อ่าน API และเอกสารประกอบ?) ประมาณครึ่งหนึ่งของการบันทึกฉันคิดว่าผิด การทดสอบเล็ก ๆ น้อย ๆ คำสั่งบันทึกใดต่อไปนี้สามารถพิมพ์ข้อยกเว้นตัวชี้โมฆะได้อย่างถูกต้อง?
ลอง {จำนวนเต็ม x = null; ++ x;} catch (exception e) {log.error (e); // a log.error (e, e); // b log.error ("" + e); // c log.error (e.toString ()); // d log.error (e.getMessage ()); // e log.error (null, e); // f log.error ("", e); // g log.error ("{}", e); // h log.error ("{}", e.getMessage ()); // i log.error ("ข้อผิดพลาดการอ่านไฟล์กำหนดค่า:" + e); // j log.error ("ข้อผิดพลาดการอ่านไฟล์กำหนดค่า:" + e.getMessage ()); // k log.error ("ข้อผิดพลาดการอ่านไฟล์กำหนดค่า", e); // l}มันแปลกที่มีเพียง G และ L (ดีกว่า) ที่ถูกต้อง! A และ B ไม่ได้รวบรวมภายใต้ SLF4J เลย คนอื่น ๆ จะทิ้งข้อมูลการติดตามสแต็กหรือพิมพ์ข้อมูลที่ไม่ถูกต้อง ตัวอย่างเช่น E ไม่ได้พิมพ์อะไรเลยเพราะข้อยกเว้นตัวชี้โมฆะนั้นไม่ได้ให้ข้อมูลข้อยกเว้นใด ๆ และข้อมูลสแต็กไม่ได้ถูกพิมพ์ออกมา โปรดจำไว้ว่าพารามิเตอร์แรกมักจะเป็นข้อมูลข้อความเกี่ยวกับข้อผิดพลาดของตัวเอง อย่าเขียนข้อมูลข้อยกเว้นในนั้น มันจะออกมาโดยอัตโนมัติหลังจากพิมพ์บันทึกหน้าข้อมูลสแต็ก แต่ถ้าคุณต้องการพิมพ์สิ่งนี้แน่นอนว่าคุณต้องผ่านข้อยกเว้นไปยังพารามิเตอร์ที่สอง
บันทึกควรอ่านและแยกวิเคราะห์ได้ง่าย
ขณะนี้มีผู้ใช้สองกลุ่มที่สนใจบันทึกของคุณ: เรามนุษย์ (ไม่ว่าคุณจะเห็นด้วยหรือไม่ก็ตามผู้เขียนโค้ดอยู่ที่นี่) และคอมพิวเตอร์ (โดยปกติจะเป็นสคริปต์เชลล์ที่เขียนโดยผู้ดูแลระบบ) บันทึกควรเหมาะสำหรับผู้ใช้ทั้งสองที่จะเข้าใจ หากมีคนดูบันทึกของโปรแกรมของคุณที่อยู่ข้างหลังคุณและเห็นสิ่งนี้:
ถ้าอย่างนั้นคุณก็ไม่ได้ทำตามคำแนะนำของฉัน บันทึกควรอ่านและเข้าใจเป็นรหัส
ในทางกลับกันหากโปรแกรมของคุณสร้างบันทึกครึ่ง GB ทุกชั่วโมงไม่มีใครหรือตัวแก้ไขข้อความกราฟิกใด ๆ ที่สามารถทำให้เสร็จได้ ในเวลานี้พวกเก่าของเรา Grep, Sed และ Awk มาเมื่อพวกเขามาที่สนาม ถ้าเป็นไปได้ดีที่สุดสำหรับบันทึกที่คุณบันทึกเพื่อทำให้ชัดเจนทั้งคอมพิวเตอร์ อย่าจัดรูปแบบตัวเลขใช้รูปแบบบางอย่างที่ทำให้สามารถจับคู่ได้เป็นประจำ ฯลฯ หากเป็นไปไม่ได้ให้พิมพ์ข้อมูลในสองรูปแบบ:
log.debug ("คำขอ ttl ตั้งค่าเป็น: {} ({})", วันที่ใหม่ (ttl), ttl); // คำขอ ttl ตั้งค่าเป็น: แต่งงาน 28 เม.ย. 20:14:12 CEST 2010 (1272478452437) {} ms ({}) ", durationmillis, ระยะเวลา); // การนำเข้าใช้: 123456789ms (1 วัน 10 ชั่วโมง 17 นาที 36 วินาที)คอมพิวเตอร์เห็น "MS After 1970 Epoch" รูปแบบเวลาดังกล่าวจะขอบคุณในขณะที่ผู้คนมีความสุขที่ได้เห็นบางอย่างเช่น "1 วัน 10 ชั่วโมง 17 นาที 36 วินาที"
ในระยะสั้นวารสารยังสามารถเขียนได้อย่างสง่างามเหมือนบทกวีถ้าคุณเต็มใจที่จะคิดเกี่ยวกับมัน
ข้างต้นคือการรวบรวมข้อมูลเกี่ยวกับรูปแบบเอาต์พุตการปรับบันทึก Java เพื่อนที่สนใจสามารถอ้างถึงได้