Tại AppSignal, chúng tôi cung cấp tính năng theo dõi lỗi cho các ứng dụng Ruby. Để làm như vậy, chúng tôi nắm bắt tất cả các trường hợp ngoại lệ mà ứng dụng gửi đến chúng tôi và thông báo cho nhà phát triển khi chúng xảy ra.
Có thể khó có thể xử lý ngoại lệ đúng cách. Trong bài viết này, chúng tôi sẽ giải thích cách thức hoạt động, những vấn đề mà việc xử lý không tốt có thể gây ra và cách giải quyết các trường hợp ngoại lệ đúng cách.
Cứu các ngoại lệ
Bằng cách giải cứu các ngoại lệ trong Ruby, bạn có thể ngăn ứng dụng của mình gặp sự cố khi có sự cố. Với begin .. rescue block, bạn có thể chỉ định một đường dẫn thay thế cho ứng dụng của mình khi xảy ra lỗi.
Cũng có thể chỉ định những trường hợp ngoại lệ nào cần được giải cứu. Khi chỉ định một lớp ngoại lệ, tất cả các lớp con của ngoại lệ này cũng sẽ bị ghi lại.
Trong ví dụ trên bạn có thể thấy ngoại lệ Errno::ENOENT bị bắt khi SystemCallError cha của nó đang được giải cứu.
Giải cứu quá cao trong chuỗi ngoại lệ
Điều quan trọng là không giải cứu các ngoại lệ ở vị trí quá cao trong chuỗi Ngoại lệ. Khi bạn làm vậy, tất cả các ngoại lệ được phân lớp con cũng sẽ bị bắt giữ, khiến cho việc nắm bắt của khối giải cứu trở nên quá chung chung.
Đây là chương trình đọc tệp cấu hình dựa trên đối số được truyền cho chương trình.
Thông báo lỗi cho biết nó không thể đọc được tệp cấu hình, nhưng vấn đề thực sự là lỗi đánh máy.
Lớp ngoại lệ mặc định được phát hiện bởi begin .. rescue khối là StandardError. Nếu chúng ta không vượt qua được một lớp cụ thể, Ruby sẽ giải cứu StandardError và tất cả các lỗi thuộc lớp con. NoMethodError là một trong những lỗi này.
Việc giải cứu một lớp ngoại lệ cụ thể sẽ giúp ngăn các lỗi không liên quan vô tình dẫn đến trạng thái lỗi. Nó cũng cho phép hiển thị các thông báo lỗi tùy chỉnh cụ thể hơn, hữu ích hơn cho người dùng cuối.
Giải cứu ngoại lệ
Việc giải cứu cấp cao trong chuỗi ngoại lệ có thể vẫn hấp dẫn. Việc giải quyết tất cả các lỗi mà ứng dụng có thể phát sinh sẽ giúp ứng dụng không bị lỗi. (Chúng tôi sẽ có 100% thời gian hoạt động!) Tuy nhiên, nó có thể gây ra nhiều vấn đề.
Lớp Exception là lớp ngoại lệ chính trong Ruby. Tất cả các trường hợp ngoại lệ khác đều là lớp con của lớp này; nếu Ngoại lệ được giải cứu thì tất cả các lỗi sẽ bị phát hiện.
Hai trường hợp ngoại lệ mà hầu hết các ứng dụng sẽ không muốn giải cứu là SignalException và SystemExit.
SignalException được sử dụng khi nguồn bên ngoài yêu cầu ứng dụng dừng lại. Đây có thể là Hệ điều hành khi muốn tắt hoặc quản trị viên hệ thống muốn dừng ứng dụng. Ví dụ
SystemExit được sử dụng khi exit đang được gọi từ ứng dụng Ruby. Khi điều này được nêu lên, nhà phát triển muốn ứng dụng dừng lại. Ví dụ
Nếu chúng tôi giải cứu Ngoại lệ và những ngoại lệ này xuất hiện trong khi ứng dụng hiện đang chạy begin ... rescue ... end chặn nó không thoát được.
Nói chung, việc giải cứu Ngoại lệ trong các tình huống thông thường là một ý tưởng tồi. Khi giải cứu Ngoại lệ, bạn sẽ ngăn không cho SignalException và SystemExit hoạt động, cũng như LoadError, SyntaxError và NoMemoryError, cùng một số lỗi khác. Thay vào đó, tốt hơn hết là giải quyết các trường hợp ngoại lệ cụ thể hơn.
Thất bại trong thử nghiệm
Khi Ngoại lệ được giải cứu, sử dụng rescue Exception => e , những thứ khác ngoài ứng dụng của bạn có thể bị hỏng. Bộ thử nghiệm thực sự có thể đang ẩn một số lỗi.
Trong các xác nhận minitest và RSpec không thành công sẽ đưa ra một ngoại lệ để thông báo cho bạn về xác nhận không thành công, không vượt qua bài kiểm tra. Khi làm vậy, chúng sẽ đưa ra các ngoại lệ tùy chỉnh của riêng mình, được phân lớp từ Ngoại lệ.
Nếu Ngoại lệ được giải cứu trong thử nghiệm hoặc trong mã ứng dụng, điều đó có thể làm im lặng lỗi xác nhận.
Dự kiến ngoại lệ
Một số mã nhằm mục đích đưa ra các ngoại lệ. Trong bộ thử nghiệm, bạn có thể chỉ cần tắt tiếng ngoại lệ để thử nghiệm không thất bại khi chúng được đưa ra.
Tuy nhiên, điều này không kiểm tra xem ngoại lệ có được đưa ra hay không. Khi ngoại lệ không được nêu ra, quá trình kiểm tra của bạn sẽ không thể biết liệu hành vi đó có còn đúng hay không.
Có thể xác nhận xem ngoại lệ có được đưa ra hay không và nếu không thì đó là ngoại lệ nào.
Tái hiện ngoại lệ
Một ứng dụng chỉ nên nắm bắt các ngoại lệ ở cấp độ cao trong chuỗi như lớp Ngoại lệ khi có lý do chính đáng. Ví dụ:khi cần thực hiện một số thao tác dọn dẹp trước khi thoát khỏi một khối mã, chẳng hạn như xóa các tệp tạm thời thực sự cần xóa.
Một đề xuất dành cho trường hợp bạn nhất thiết phải giải cứu Ngoại lệ, hãy thực hiện lại ngoại lệ đó sau khi bạn xử lý xong lỗi. Bằng cách này, việc xử lý ngoại lệ của Ruby có thể quyết định số phận của quá trình sau đó.
Không biết phải giải cứu cái gì?
Như đã đề cập trước đó, bạn nên nêu cụ thể những lỗi cần khắc phục.
Khi bạn không chắc chắn về ngoại lệ nào mà một thao tác có thể đưa ra, việc giải cứu StandardError có thể là một cách tốt để bắt đầu. Chạy mã của bạn trong các tình huống khác nhau và xem nó phát sinh những ngoại lệ nào.
Mỗi khi bạn gặp một ngoại lệ mới, hãy thêm các trường hợp giải cứu cụ thể cho các ngoại lệ đó hoặc lớp cha có liên quan của nó. Tốt hơn là nên nêu cụ thể những gì cần giải cứu hơn là giải cứu quá nhiều trường hợp ngoại lệ.
Điều này kết thúc phần sơ lược của chúng ta về cách xử lý ngoại lệ trong Ruby. Hãy cho chúng tôi biết tại @AppSignal nếu bạn muốn biết thêm hoặc có câu hỏi cụ thể. Nếu bạn muốn hiểu rõ hơn về vị trí và tần suất ngoại lệ được đưa ra trong ứng dụng của mình, hãy dùng thử AppSignal.