เหตุใด "บันทึกและโยน" จึงถือเป็นการต่อต้านรูปแบบ [ปิด]


89

คำถามนี้เกิดจากการอภิปรายในบทความนี้ซึ่งฉันไม่ได้รับคำตอบที่ดีเลย

เหตุใดจึงควรบันทึกข้อยกเว้นของคุณแล้วสร้างใหม่ (แน่นอนว่าการรักษาสแต็กติดตามเดิม) เป็นความคิดที่ไม่ดีหากคุณไม่สามารถจัดการได้


ลองค้นหาคำตอบบนsoftwareengineering.stackexchange.com
chharvey

คำตอบ:


129

ฉันคิดว่าคำตอบส่วนใหญ่เป็นเพราะเหตุใดคุณจึงจับได้ถ้าคุณไม่สามารถจัดการได้ ทำไมไม่ปล่อยให้ใครก็ตามสามารถจัดการมันได้ (หรือใครก็ตามที่ไม่มีทางเลือกอื่นนอกจากจัดการมัน) บันทึกมันถ้าพวกเขารู้สึกว่ามันคุ้มค่ากับการเข้าสู่ระบบ?

หากคุณจับได้และบันทึกและสร้างใหม่จะไม่มีทางที่รหัสต้นน้ำจะรู้ว่าคุณได้บันทึกข้อยกเว้นแล้วดังนั้นข้อยกเว้นเดียวกันอาจถูกบันทึกสองครั้ง หรือแย่กว่านั้นถ้ารหัสอัปสตรีมทั้งหมดเป็นไปตามรูปแบบเดียวกันนี้ข้อยกเว้นอาจถูกบันทึกเป็นจำนวนครั้งโดยพลการหนึ่งครั้งสำหรับแต่ละระดับในรหัสที่ตัดสินใจที่จะจับมันบันทึกแล้วโยนอีกครั้ง

นอกจากนี้บางคนอาจโต้แย้งว่าเนื่องจากข้อยกเว้นการขว้างปาและการจับถือเป็นการดำเนินการที่ค่อนข้างมีค่าใช้จ่ายสูงการจับและการปลูกใหม่ทั้งหมดนี้ไม่ได้ช่วยประสิทธิภาพรันไทม์ของคุณ และไม่ได้ช่วยโค้ดของคุณในแง่ของความกระชับหรือการบำรุงรักษา


สำหรับรายละเอียดเพิ่มเติมโปรดดูความคิดเห็นเกี่ยวกับคำตอบของ Jeff
Manu

8
เหตุผลในการบันทึกและลบล้างการลบล้างซึ่งจะเป็นการ จำกัด เหตุผลให้แคบลงและบันทึกข้อความเฉพาะ
Mike Argyriou

12
คำตอบ: อาจมีข้อมูลการดีบักที่เป็นประโยชน์ ณ จุดปัจจุบันในสแต็กซึ่งจะไม่สามารถใช้งานได้ที่จุดอื่นในการติดตามสแต็ก
rtconner

บางครั้งการจับและการเปลี่ยนใหม่อาจเป็นประโยชน์หากข้อยกเว้นใหม่มีประโยชน์มากขึ้นโดยมีข้อมูลเพิ่มเติมหรือเฉพาะเจาะจงมากขึ้น
borjab

1
เพื่อใช้จ่ายกับสิ่งที่ @rtconner แสดงความคิดเห็น: ในรหัส async อาจเป็นไปได้ว่า catcher ไม่มีบริบทที่ผู้ขว้างปาสามารถใช้ได้
Nir Alfasi

55

Log-and-throw เป็นรูปแบบที่ดีหากเอนทิตีที่จับและสร้างข้อยกเว้นขึ้นมาใหม่มีเหตุผลที่เชื่อได้ว่ามีข้อมูลที่จะไม่ถูกบันทึกเพิ่มเติมในกลุ่มการโทร - อย่างน้อยก็ไม่ใช่ในรูปแบบที่ต้องการมากที่สุด สาเหตุสองประการนี้อาจเกิดขึ้น:

  1. ข้อยกเว้นอาจถูกจับและเปลี่ยนใหม่ที่ขอบเขตของชั้นแอปพลิเคชันและอาจมีข้อมูลที่เป็นสิทธิพิเศษ มันจะเป็นการไม่ดีสำหรับเลเยอร์ฐานข้อมูลที่อนุญาตให้มีข้อยกเว้นเช่น "พยายามเพิ่มคีย์ที่ซ้ำกัน 'fnord' ลงในฟิลด์ 'ผู้ใช้' เพื่อเข้าถึงเลเยอร์แอปพลิเคชันภายนอก (ซึ่งอาจทำให้ผู้ใช้เห็น) แต่ อาจมีประโยชน์สำหรับส่วนภายในของฐานข้อมูลในการทิ้งข้อยกเว้นดังกล่าวและอินเทอร์เฟซของแอปพลิเคชันเพื่อตรวจจับบันทึกอย่างปลอดภัยและสร้างข้อยกเว้นที่อธิบายได้น้อยกว่า
  2. ข้อยกเว้นอาจเป็นข้อยกเว้นที่ชั้นนอกคาดว่าจะจัดการได้โดยไม่ต้องบันทึก แต่ชั้นในอาจรู้บางอย่างที่ชั้นนอกไม่แนะนำว่าการบันทึกอาจเป็นประโยชน์ ตามตัวอย่างคร่าวๆอาจมีการตั้งโปรแกรมเลเยอร์แอปพลิเคชันกลางให้ลองเชื่อมต่อกับเซิร์ฟเวอร์หนึ่งและหากไม่ได้ผลให้ลองอีกครั้ง การทำให้บันทึกของแอปพลิเคชันเต็มไปด้วยข้อความ "การเชื่อมต่อล้มเหลว" ในขณะที่เซิร์ฟเวอร์หยุดทำงานเพื่อการบำรุงรักษาอาจไม่เป็นประโยชน์โดยเฉพาะอย่างยิ่งเนื่องจาก - จากมุมมองของแอปพลิเคชันทุกอย่างทำงานได้ดี อาจเป็นประโยชน์ในการส่งต่อข้อมูลเกี่ยวกับความล้มเหลวในการเชื่อมต่อไปยังทรัพยากรการบันทึกที่เชื่อมโยงกับเซิร์ฟเวอร์ซึ่งจะสามารถกรองบันทึกเพื่อสร้างรายงานเมื่อเซิร์ฟเวอร์ขึ้นและลงเมื่อเทียบกับบันทึกของทุกครั้งที่พยายามเชื่อมต่อ .

4
# 1 เป็นกรณีการใช้งานทั่วไปที่สมเหตุสมผล แต่ OP ถามอย่างชัดเจนเกี่ยวกับ "การปลูกใหม่ (แน่นอนว่าเป็นการรักษาร่องรอยสแต็กเดิม)" ดังนั้น # 1 จึงไม่ใช่คำตอบที่ถูกต้อง
Geoffrey Zheng

@GeoffreyZheng: ขึ้นอยู่กับว่าการบันทึกการติดตามสแต็กเดิมอย่างปลอดภัยจะนับเป็นการ "เก็บรักษา" หรือไม่
supercat

3
เกี่ยวกับ # 1: การเปิดเผยเนื้อหาข้อยกเว้น (เช่นการแสดงe.getMessage()/ stacktrace บน UI รวมถึงการส่งเป็นการตอบกลับ REST) ​​ควรถือเป็นช่องโหว่เนื่องจากข้อยกเว้นรันไทม์อาจมีข้อมูลที่ละเอียดอ่อนประเภทใดก็ได้ เกี่ยวกับ # 2: คุณสามารถโยนข้อยกเว้นอีกครั้งโดยเพิ่มข้อมูลทั้งหมดที่คุณต้องการให้ลูกค้าทราบ (+ สาเหตุที่แท้จริง) โดยไม่จำเป็นต้องบันทึกอะไรเลย
Nikita Bosik

@NikitaBosik: ไม่ว่าจะมีการเปิดเผยข้อยกเว้นในฝั่งไคลเอ็นต์หรือไม่แอปพลิเคชันที่ทำเช่นนั้นเป็นเรื่องธรรมดาที่เพียงพอที่หลักการ 'การรักษาความปลอดภัยในเชิงลึก' จะแนะนำว่าข้อความข้อยกเว้นควรถูกลบออกจากข้อมูลที่ละเอียดอ่อน นอกจากนี้หากชั้นนอกไม่คาดว่าจะทิ้งข้อยกเว้นบางประเภทโดยไม่บันทึกหรือส่งต่อข้อมูลไปยังหน่วยงานภายนอกที่อาจสนใจการมีชั้นกลางเพิ่มข้อมูลที่ชั้นนอกจะเพิกเฉยจะไม่ช่วย อะไรก็ได้
supercat

20

ฉันเดาว่าเหตุผลที่ง่ายที่สุดคือโดยทั่วไปคุณจะมีตัวจัดการระดับบนสุดเพียงคนเดียวที่ทำสิ่งนี้ให้คุณดังนั้นจึงไม่จำเป็นต้องสร้างมลพิษให้กับโค้ดด้วยการจัดการข้อยกเว้นนี้

ข้อโต้แย้งที่เกี่ยวข้องกับการตัดต่อนั้นโดยพื้นฐานแล้วเป็นการเสียเวลาในการจัดการข้อผิดพลาดที่ไม่เกี่ยวข้องกับคุณ ดีกว่าที่จะปล่อยให้ข้อผิดพลาดเกิดขึ้นในกลุ่มการโทรจนกว่าจะพบตัวจัดการที่เหมาะสม

ในความคิดของฉันครั้งเดียวที่คุณควรได้รับข้อยกเว้นคือเมื่อคุณสามารถทำสิ่งที่เป็นประโยชน์กับผลลัพธ์ได้ การจับเพื่อบันทึกไม่เป็นประโยชน์เพราะคุณสามารถรวมศูนย์การทำงานนั้นขึ้นไปได้


2
ฉันยอมรับว่าคุณควรมีตัวจัดการระดับบนสุดเพียงคนเดียวที่ทำสิ่งนี้ให้คุณ แต่ในความคิดของฉันผู้ดูแลระดับบนสุดควรเป็น "บันทึกแล้วโยน" ดังนั้นอาร์กิวเมนต์จึงอยู่ที่จุดที่จะ "เข้าสู่ระบบและโยน" ไม่ใช่หรือไม่ทำเลย!?
Manu

@Manu ฉันเดาประเด็นคือถ้าเป็นตัวจัดการเดียว (รวมศูนย์) ก็ไม่เป็นไร หากคุณกำลังทำรหัสซ้ำก็ไม่เป็นไร ฉันไม่คิดว่าจะมีอะไรมากกว่านั้น!
Jeff Foster

5
@Manu - ตัวจัดการระดับสูงที่โยนไปให้ในกรณีนั้นคืออะไร? สำหรับฉันแล้วดูเหมือนว่าหากมีบางสิ่งที่สามารถส่งไปได้นั่นก็ไม่ใช่ตัวจัดการระดับบนสุด และคุณไม่ต้องการที่จะสร้างข้อยกเว้นขึ้นมาใหม่ในสภาพแวดล้อมรันไทม์ ซึ่งจะทำให้แอปพลิเคชันส่วนใหญ่หยุดชะงัก
aroth

1
@aroth: ฉันเข้าใจว่าสิ่งที่คุณพูดคือ "บันทึกและจัดการ" (ถ้าคุณเป็นตัวจัดการระดับบนสุด) หรือ "อย่าบันทึกและโยน (หรือจัดการอย่างอื่น)" ถ้าคุณไม่ได้ ขอบคุณที่ชี้ให้เห็น
Manu

9

การบันทึกและโยน IMO เป็นการละเมิดหลักการของการเซอร์ไพรส์น้อยที่สุดอย่างชัดเจน

หากข้อยกเว้นได้รับการจัดการอย่างเหมาะสมต่อไปใน call stack มากขึ้นอาจไม่คุ้มค่ากับรายการบันทึกข้อผิดพลาดเลย จากนั้นจึงเกิดความสับสนในการค้นหารายการบันทึกข้อผิดพลาด


สิ่งที่เกี่ยวกับบันทึกที่ไม่ใช่ข้อผิดพลาด
Su Zhang

6
@SuZhang ฉันไม่ค่อยเข้าใจคำถามของคุณ หากไม่มีข้อผิดพลาดก็ไม่มีอะไรให้โยน แน่นอนคุณสามารถและควรเขียนบันทึกที่ไม่ใช่ข้อผิดพลาด
Bastian Voigt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.