Kiến trúc nhiều lớp: nơi tôi nên thực hiện ghi nhật ký \ xử lý lỗi?


17

Tôi hiện đang cấu trúc lại một hệ thống con lớn với kiến ​​trúc nhiều lớp và tôi đang vật lộn để thiết kế một chiến lược xử lý lỗi ghi nhật ký \ xử lý lỗi hiệu quả.

Hãy nói rằng kiến ​​trúc của tôi bao gồm ba lớp sau:

  • Giao diện công cộng (IE một bộ điều khiển MVC)
  • Lớp miền
  • Lớp truy cập dữ liệu

Nguồn gây nhầm lẫn của tôi là nơi tôi nên thực hiện ghi nhật ký lỗi \ xử lý:

  1. Giải pháp đơn giản nhất là triển khai ghi nhật ký ở cấp cao nhất (IE Bộ điều khiển giao diện công cộng \ MVC). Tuy nhiên, điều này cảm thấy sai bởi vì nó có nghĩa là tạo ra ngoại lệ thông qua các lớp khác nhau, và sau đó đăng nhập nó; thay vì đăng nhập ngoại lệ tại nguồn của nó.

  2. Ghi nhật ký ngoại lệ tại nguồn của nó rõ ràng là giải pháp tốt nhất vì tôi có nhiều thông tin nhất. Vấn đề của tôi với điều này là tôi không thể bắt được mọi ngoại lệ tại nguồn mà không bắt TẤT CẢ các ngoại lệ và trong lớp giao diện công cộng / miền, điều này sẽ dẫn đến việc bắt các ngoại lệ đã bị bắt, ghi lại và ném lại bởi lớp bên dưới .

  3. Một chiến lược khả thi khác là sự pha trộn giữa # 1 và # 2; nhờ đó tôi bắt được các ngoại lệ cụ thể ở lớp mà chúng có khả năng bị ném nhất (Bắt IE, ghi nhật ký và ném lại SqlExceptionstrong Lớp truy cập dữ liệu) và sau đó ghi lại bất kỳ ngoại lệ nào chưa được phát hiện ở cấp cao nhất. Tuy nhiên, điều này cũng đòi hỏi tôi phải nắm bắt và loại bỏ mọi ngoại lệ ở cấp cao nhất, bởi vì tôi không thể phân biệt giữa các lỗi đã được ghi lại \ xử lý so với các lỗi không xảy ra.

Bây giờ, rõ ràng đây là một vấn đề trong hầu hết các ứng dụng phần mềm, vì vậy phải có một giải pháp chuẩn cho vấn đề này dẫn đến các ngoại lệ bị bắt tại nguồn và được ghi lại một lần; tuy nhiên tôi không thể thấy cách tự làm việc này.

Lưu ý, tiêu đề của câu hỏi này rất giống với ' Ghi nhật ký ngoại lệ trong ứng dụng nhiều tầng " ', tuy nhiên, câu trả lời trong bài đăng đó thiếu chi tiết và không đủ để trả lời câu hỏi của tôi.


1
Một cách tiếp cận mà đôi khi tôi sử dụng là tạo lớp con Exception (hoặc RuntimeException) của riêng tôi và ném nó, bao gồm cả ngoại lệ ban đầu là một lớp lồng nhau. Tất nhiên, điều đó có nghĩa là khi nắm bắt các ngoại lệ ở cấp độ cao hơn, tôi cần kiểm tra loại ngoại lệ (ngoại lệ của riêng tôi vừa được rút lại, các ngoại lệ khác được ghi lại và đưa vào các trường hợp ngoại lệ mới của tôi). Nhưng tôi đã làm việc một mình trong một thời gian dài vì vậy tôi không thể đưa ra lời khuyên "chính thức".
SJuan76

4
The easiest solution would be to implement the logging at the top level- làm cái này. Ghi nhật ký ngoại lệ tại nguồn của họ không phải là một ý tưởng hay và mọi ứng dụng tôi gặp phải đều là PITA để gỡ lỗi. Nó phải là trách nhiệm của người gọi để xử lý các trường hợp ngoại lệ.
Justin

Bạn dường như có một số kỹ thuật / ngôn ngữ thực hiện cụ thể trong tâm trí, nếu không, câu nói "ngoại lệ được ghi lại không thể phân biệt được với những từ không" là khó hiểu. Bạn có thể cho thêm bối cảnh?
Vroomfondel

@Vroomfondel - Bạn nói đúng. Tôi đang sử dụng C # và gói mã trong mỗi Lớp try{ ... } catch(Exception ex) { Log(ex); }sẽ dẫn đến cùng một ngoại lệ được ghi lại ở mỗi lớp. (Có vẻ như thực tế khá tệ khi bắt mọi ngoại lệ ở mọi lớp trong cơ sở mã.)
KidCode

Câu trả lời:


18

Đối với câu hỏi của bạn:

Giải pháp đơn giản nhất là thực hiện ghi nhật ký ở cấp cao nhất

Có bong bóng ngoại lệ lên đến cấp cao nhất là một cách tiếp cận hoàn toàn chính xác và hợp lý. Không có phương pháp lớp cao hơn nào cố gắng tiếp tục một số quy trình sau thất bại, thường không thể thành công. Và một ngoại lệ được trang bị tốt chứa tất cả các thông tin cần thiết để đăng nhập. Và không làm gì về các ngoại lệ giúp bạn giữ mã của mình sạch sẽ và tập trung vào nhiệm vụ chính thay vì các thất bại.

Ghi nhật ký ngoại lệ tại nguồn của nó rõ ràng là giải pháp tốt nhất vì tôi có nhiều thông tin nhất.

Điều đó đúng một nửa. Vâng, hầu hết các thông tin hữu ích có sẵn ở đó. Nhưng tôi khuyên bạn nên đặt tất cả những thứ này vào đối tượng ngoại lệ (nếu nó chưa có ở đó) thay vì đăng nhập ngay lập tức. Nếu bạn đăng nhập ở mức thấp, bạn vẫn cần đưa ra một ngoại lệ để nói với người gọi rằng bạn đã không hoàn thành công việc của mình. Điều này kết thúc trong nhiều bản ghi của cùng một sự kiện.

Ngoại lệ

Hướng dẫn chính của tôi là chỉ bắt và ghi nhật ký ngoại lệ ở cấp cao nhất. Và tất cả các lớp bên dưới phải đảm bảo rằng tất cả các thông tin lỗi cần thiết được vận chuyển lên cấp cao nhất. Trong một ứng dụng một quy trình, ví dụ như trong Java, điều này chủ yếu có nghĩa là không thử / bắt hoặc đăng nhập ở bên ngoài cấp cao nhất.

Đôi khi, bạn muốn một số thông tin ngữ cảnh được bao gồm trong nhật ký ngoại lệ không có trong ngoại lệ ban đầu, ví dụ: câu lệnh SQL và các tham số đã được thực thi khi ném ngoại lệ. Sau đó, bạn có thể bắt ngoại lệ ban đầu và ném lại một cái mới, chứa cái ban đầu cộng với bối cảnh.

Tất nhiên, cuộc sống thực đôi khi can thiệp:

  • Trong Java, đôi khi bạn phải bắt một ngoại lệ và gói nó thành một loại ngoại lệ khác chỉ để tuân theo một số chữ ký phương thức cố định. Nhưng nếu bạn ném lại một ngoại lệ, hãy đảm bảo rằng một lần ném lại chứa tất cả thông tin cần thiết để đăng nhập sau này.

  • Nếu bạn đang vượt qua một biên giới giữa các quá trình, về mặt kỹ thuật, bạn thường không thể chuyển toàn bộ đối tượng ngoại lệ bao gồm cả dấu vết ngăn xếp. Và tất nhiên kết nối có thể bị mất. Vì vậy, đây là điểm mà một dịch vụ nên ghi lại các trường hợp ngoại lệ và sau đó cố gắng hết sức để truyền tải càng nhiều thông tin lỗi càng tốt qua đường dây đến máy khách của nó. Dịch vụ phải đảm bảo khách hàng nhận được thông báo lỗi, bằng cách nhận được phản hồi lỗi hoặc bằng cách chạy vào thời gian chờ trong trường hợp kết nối bị hỏng. Điều này thường sẽ dẫn đến cùng một lỗi không được ghi lại hai lần, một lần trong dịch vụ (với nhiều chi tiết hơn) và một lần ở cấp cao nhất của khách hàng.

Ghi nhật ký

Tôi đang thêm một số câu về ghi nhật ký nói chung, không chỉ ghi nhật ký ngoại lệ.

Bên cạnh các tình huống đặc biệt, bạn cũng muốn các hoạt động quan trọng của ứng dụng của mình được ghi lại trong nhật ký. Vì vậy, sử dụng một khung đăng nhập.

Hãy cẩn thận về các mức nhật ký (đọc nhật ký trong đó thông tin gỡ lỗi và các lỗi nghiêm trọng không được gắn cờ với khác nhau là một nỗi đau!). Các cấp độ nhật ký điển hình là:

  • LRI: Một số chức năng không thể phục hồi. Điều đó không nhất thiết có nghĩa là toàn bộ chương trình của bạn bị sập, nhưng một số nhiệm vụ không thể hoàn thành. Thông thường, bạn có một đối tượng ngoại lệ mô tả sự thất bại.
  • CẢNH BÁO: Một cái gì đó kỳ lạ đã xảy ra, nhưng không gây ra bất kỳ tác vụ nào (cấu hình lạ được phát hiện, sự cố kết nối tạm thời gây ra một số lần thử lại, v.v.)
  • THÔNG TIN: Bạn muốn truyền đạt một số hành động quan trọng của chương trình tới quản trị viên hệ thống cục bộ (bắt đầu một số dịch vụ với cấu hình và phiên bản phần mềm, nhập tệp dữ liệu vào cơ sở dữ liệu, người dùng đăng nhập vào hệ thống, bạn có ý tưởng ...).
  • DEBUG: Những điều bạn là nhà phát triển muốn thấy khi bạn gỡ lỗi một số vấn đề (nhưng bạn sẽ không bao giờ biết trước những gì bạn thực sự cần trong trường hợp này hoặc lỗi cụ thể đó - nếu bạn có thể thấy trước, bạn sẽ sửa lỗi ). Một điều luôn hữu ích là ghi nhật ký các hoạt động trên các giao diện bên ngoài.

Trong sản xuất, đặt mức ghi nhật ký thành INFO. Các kết quả sẽ hữu ích cho quản trị viên hệ thống để anh ta biết chuyện gì đang xảy ra. Mong anh ấy gọi cho bạn để được hỗ trợ hoặc sửa lỗi cho mọi LRI trong nhật ký và một nửa CẢNH BÁO.

Chỉ kích hoạt mức DEBUG trong các phiên gỡ lỗi thực.

Nhóm các mục nhật ký thành các danh mục phù hợp (ví dụ: theo tên lớp đủ điều kiện của mã tạo ra mục nhập), cho phép bạn bật nhật ký gỡ lỗi cho các phần cụ thể của chương trình.


Cảm ơn cho một câu trả lời chi tiết như vậy, tôi thực sự đánh giá cao phần đăng nhập quá.
KidCode

-1

Tôi đang chuẩn bị tinh thần cho các downvote, nhưng tôi sẽ đi ra ngoài và nói rằng tôi không chắc mình có thể đồng ý với điều này.

Bong bóng ngoại lệ lên, ít đăng nhập lại chúng, là nỗ lực thêm với ít lợi ích. Bắt ngoại lệ tại nguồn (có, dễ nhất), ghi nhật ký, nhưng sau đó không ném lại ngoại lệ, chỉ báo cáo "lỗi" cho người gọi. "-1", null, chuỗi rỗng, một số enum, bất cứ điều gì. Người gọi chỉ cần biết rằng cuộc gọi thất bại, hầu như không bao giờ là chi tiết khủng khiếp. Và những cái đó sẽ có trong nhật ký của bạn, phải không? Trong trường hợp hiếm hoi mà người gọi không cần các chi tiết, hãy tiếp tục và nổi bong bóng, nhưng không phải là một mặc định không suy nghĩ tự động.


3
Vấn đề với việc báo cáo lỗi bằng các giá trị trả về là: 1. Nếu người gọi quan tâm đến các lỗi, nó phải kiểm tra giá trị đặc biệt. Nhưng chờ đã, nó là nullchuỗi rỗng? Là -1 hoặc bất kỳ số âm nào? 2. Nếu người gọi không quan tâm (tức là không kiểm tra), điều này dẫn đến việc theo dõi các lỗi không liên quan đến nguyên nhân ban đầu, ví dụ a NullPointerException. Hoặc tệ hơn: Tính toán tiếp tục với các giá trị sai. 3. Nếu người gọi quan tâm nhưng lập trình viên không nghĩ rằng phương pháp này bị lỗi, trình biên dịch sẽ không nhắc nhở anh ta. Trường hợp ngoại lệ không có vấn đề này, hoặc bạn bắt hoặc bạn suy nghĩ lại.
siegi

1
Xin lỗi, tôi đã phải downvote đó. Cách tiếp cận mà bạn mô tả (thay thế các ngoại lệ bằng các giá trị trả về đặc biệt) là công nghệ tiên tiến vào những năm 1970, khi ngôn ngữ C bắt nguồn và có nhiều lý do chính đáng tại sao các ngôn ngữ hiện đại có ngoại lệ, và đối với tôi, đó là lý do chính là sử dụng ngoại lệ đúng cách giúp viết mã mạnh mẽ dễ dàng hơn nhiều. Và "Bong bóng ngoại lệ lên [...] là một nỗ lực thêm [...]" hoàn toàn sai: chỉ cần không làm gì về ngoại lệ, và chúng sẽ tự bong bóng lên.
Ralf Kleberhoff
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.