สถาปัตยกรรมแบบหลายชั้น: สถานที่ที่ฉันควรใช้การบันทึกข้อผิดพลาด \ การจัดการ?


17

ขณะนี้ฉันกำลังปรับโครงสร้างระบบย่อยขนาดใหญ่ด้วยสถาปัตยกรรมหลายเลเยอร์และฉันพยายามดิ้นรนเพื่อออกแบบกลยุทธ์การบันทึกข้อผิดพลาด \ การจัดการที่มีประสิทธิภาพ

สมมติว่าสถาปัตยกรรมของฉันประกอบด้วยสามชั้นต่อไปนี้:

  • ส่วนต่อประสานสาธารณะ (IE เป็น MVC Controller)
  • เลเยอร์โดเมน
  • ชั้นการเข้าถึงข้อมูล

แหล่งที่มาของความสับสนของฉันคือที่ฉันควรใช้การบันทึกข้อผิดพลาด \ การจัดการ:

  1. ทางออกที่ง่ายที่สุดคือการใช้การบันทึกที่ระดับบนสุด (IE the Public Interface \ MVC Controller) อย่างไรก็ตามสิ่งนี้รู้สึกผิดเพราะมันหมายถึงการทำให้ข้อยกเว้นผ่านเลเยอร์ที่แตกต่างกันแล้วบันทึกมัน แทนที่จะบันทึกข้อยกเว้นที่แหล่งที่มา

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

  3. อีกกลยุทธ์ที่เป็นไปได้คือการผสมผสานของ # 1 และ # 2; โดยที่ฉันตรวจจับข้อยกเว้นเฉพาะที่เลเยอร์พวกเขามักจะถูกโยน (IE Catching, การบันทึกและการโยนอีกครั้งSqlExceptionsใน Data Access Layer) แล้วบันทึกข้อยกเว้นที่ไม่ถูกตรวจจับเพิ่มเติมที่ระดับบนสุด อย่างไรก็ตามสิ่งนี้จะทำให้ฉันต้องจับและเชื่อมต่อใหม่ทุกข้อยกเว้นที่ระดับบนสุดเพราะฉันไม่สามารถแยกแยะความผิดพลาดระหว่างข้อผิดพลาดที่ได้รับการจัดการ \ จัดการกับข้อผิดพลาดที่ไม่มี

ตอนนี้เห็นได้ชัดว่านี่เป็นปัญหาในแอปพลิเคชั่นซอฟต์แวร์ส่วนใหญ่ดังนั้นจึงต้องมีวิธีแก้ไขปัญหามาตรฐานที่ทำให้เกิดข้อยกเว้นในการจับแหล่งที่มาและบันทึกครั้งเดียว แต่ฉันไม่สามารถเห็นวิธีการทำสิ่งนี้ด้วยตัวเอง

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


1
วิธีการหนึ่งที่ฉันใช้ในบางครั้งคือการสร้างคลาสย่อย Exception (หรือ RuntimeException) ของตัวเองแล้วโยนมันรวมถึงข้อยกเว้นดั้งเดิมเป็นแบบซ้อน แน่นอนมันหมายความว่าเมื่อจับภาพข้อยกเว้นในระดับบนฉันต้องตรวจสอบประเภทของข้อยกเว้น (ข้อยกเว้นของฉันเพิ่งได้รับการ rethrown ข้อยกเว้นอื่น ๆ จะถูกบันทึกและรวมอยู่ในอินสแตนซ์ใหม่ของข้อยกเว้นของฉัน) แต่ฉันทำงานเดี่ยวมาเป็นเวลานานดังนั้นฉันจึงไม่สามารถให้คำแนะนำ "ทางการ" ได้
SJuan76

4
The easiest solution would be to implement the logging at the top level- ทำเช่นนี้. การบันทึกข้อยกเว้นที่แหล่งที่มาของพวกเขาไม่ใช่ความคิดที่ดีและทุกแอปพลิเคชันที่ฉันพบนั่นเป็น PITA ในการดีบัก ควรเป็นความรับผิดชอบของผู้โทรในการจัดการข้อยกเว้น
Justin

คุณดูเหมือนจะมีเทคนิคการใช้งานเฉพาะ / ภาษาอยู่ในใจมิฉะนั้นคำสั่งของคุณ "ข้อยกเว้นซึ่งถูกบันทึกไว้จะไม่สามารถแยกแยะได้จากสิ่งที่ไม่ใช่" นั้นยากที่จะเข้าใจ คุณสามารถให้บริบทเพิ่มเติมได้หรือไม่
Vroomfondel

@Vroomfondel - คุณพูดถูก ฉันใช้ C # และการวางโค้ดในแต่ละเลเยอร์ด้วยtry{ ... } catch(Exception ex) { Log(ex); }จะส่งผลให้เกิดข้อยกเว้นเดียวกันกับที่ถูกบันทึกไว้ในแต่ละเลเยอร์ (ดูเหมือนจะเป็นการฝึกที่ไม่ดีนักที่จะจับทุกข้อยกเว้นในทุกเลเยอร์ในฐานรหัส)
KidCode

คำตอบ:


18

สำหรับคำถามของคุณ:

ทางออกที่ง่ายที่สุดคือการใช้การบันทึกที่ระดับบนสุด

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

การบันทึกข้อยกเว้นที่แหล่งที่มานั้นเป็นทางออกที่ดีที่สุดเพราะฉันมีข้อมูลมากที่สุด

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

ข้อยกเว้น

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

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

แน่นอนว่าชีวิตจริงบางครั้งรบกวน:

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

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

เข้าสู่ระบบ

ฉันกำลังเพิ่มประโยคบางอย่างเกี่ยวกับการเข้าสู่ระบบโดยทั่วไปไม่เพียง แต่การบันทึกข้อยกเว้น

นอกจากสถานการณ์พิเศษแล้วคุณต้องการให้กิจกรรมที่สำคัญของแอปพลิเคชันของคุณได้รับการบันทึกในบันทึกด้วย ดังนั้นให้ใช้เฟรมเวิร์กการบันทึก

ระมัดระวังเกี่ยวกับระดับการบันทึก (การอ่านบันทึกที่ข้อมูลการแก้ปัญหาและข้อผิดพลาดร้ายแรงไม่ได้ตั้งค่าสถานะที่แตกต่างกันตามความเจ็บปวด!) ระดับบันทึกทั่วไปคือ:

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

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

เปิดใช้งานระดับ DEBUG ในระหว่างเซสชันการดีบักจริงเท่านั้น

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


ขอบคุณสำหรับคำตอบอย่างละเอียดฉันขอขอบคุณส่วนการบันทึกด้วยเช่นกัน
KidCode

-1

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

ข้อยกเว้นเดือดปุด ๆ มากน้อยเข้าสู่ระบบพวกเขาอีกครั้งเป็นความพยายามพิเศษที่มีประโยชน์น้อย จับข้อยกเว้นที่แหล่งที่มา (ใช่ง่ายที่สุด) เข้าสู่ระบบ แต่จากนั้นอย่าโยนข้อยกเว้นอีกครั้งเพียงรายงาน "ข้อผิดพลาด" ไปยังผู้โทร "-1", null, สตริงว่าง, บาง enum, อะไรก็ตาม ผู้โทรเพียงแค่ต้องรู้ว่าการโทรล้มเหลวแทบไม่เคยมีรายละเอียดที่น่ากลัว และสิ่งเหล่านั้นจะอยู่ในบันทึกของคุณใช่ไหม ในกรณีที่เกิดขึ้นได้ยากที่ผู้โทรจำเป็นต้องใช้รายละเอียดให้ดำเนินการต่อไปและทำให้ฟองสบู่ขึ้น แต่จะไม่เป็นค่าเริ่มต้นที่ไม่ได้คิดโดยอัตโนมัติ


3
ปัญหาเกี่ยวกับการรายงานข้อผิดพลาดโดยค่าส่งคืนคือ: 1. หากผู้โทรใส่ใจกับความล้มเหลวก็จะต้องตรวจสอบค่าพิเศษ แต่เดี๋ยวก่อนมันเป็นnullหรือเปล่าสตริง? มันคือ -1 หรือจำนวนลบใด ๆ 2. ถ้าโทรไม่สนใจ (เช่นไม่ได้ตรวจสอบ) NullPointerExceptionนำไปสู่การนี้เพื่อติดตามข้อผิดพลาดที่ไม่เกี่ยวข้องกับสาเหตุเดิมเช่น หรือแย่กว่านั้น: การคำนวณยังคงมีค่าที่ผิด 3. หากผู้โทรเข้ามาสนใจ แต่โปรแกรมเมอร์ไม่คิดว่าวิธีการนี้จะล้มเหลวตัวแปลภาษาจะไม่เตือนเขา ข้อยกเว้นไม่มีปัญหานี้ไม่ว่าคุณจะจับหรือคุณสร้างใหม่
siegi

1
ขออภัยฉันต้องลงคะแนนว่า วิธีการที่คุณอธิบาย (แทนที่ข้อยกเว้นด้วยค่าส่งคืนพิเศษ) คือ state-of-the-art ในปี 1970 เมื่อภาษา C มีต้นกำเนิดและมีหลายเหตุผลที่ดีว่าทำไมภาษาสมัยใหม่จึงมีข้อยกเว้นและสำหรับฉันหลักสำคัญคือ การใช้ข้อยกเว้นอย่างถูกต้องทำให้การเขียนโค้ดที่มีประสิทธิภาพทำได้ง่ายขึ้นมาก และ "การยกเว้น Bubbling up [... ] เป็นความพยายามพิเศษ [... ]" ของคุณนั้นผิดธรรมดา: เพียงแค่ไม่ทำอะไรเลยกับข้อยกเว้น
Ralf Kleberhoff
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.