AppSignal पर हम रूबी अनुप्रयोगों के लिए त्रुटि ट्रैकिंग प्रदान करते हैं। ऐसा करने के लिए, हम उन सभी अपवादों को पकड़ते हैं जो हमारे ऊपर फेंके जाते हैं और डेवलपर्स को सूचित करते हैं जैसे वे होते हैं।
अपवाद हैंडलिंग को सही तरीके से प्राप्त करना मुश्किल हो सकता है। इस लेख में हम बताएंगे कि यह कैसे काम करता है, खराब प्रबंधन किन समस्याओं का कारण बन सकता है और अपवादों को ठीक से कैसे बचाया जाए।
बचाव अपवाद
रूबी में अपवादों को बचाकर आप कुछ गलत होने पर अपने एप्लिकेशन को क्रैश होने से रोक सकते हैं। एक begin .. rescue
. के साथ जब कोई त्रुटि होती है तो आप अपने आवेदन के लिए वैकल्पिक पथ निर्दिष्ट कर सकते हैं।
begin
File.read "config.yml"
rescue
puts "No config file found. Using defaults."
end
यह निर्दिष्ट करना भी संभव है कि कौन से अपवादों को बचाया जाना चाहिए। अपवाद वर्ग निर्दिष्ट करते समय, इस अपवाद के सभी उपवर्गों को भी कैप्चर किया जाएगा।
begin
File.read "config.yml"
rescue SystemCallError => e
puts e.class # => Errno::ENOENT
puts e.class.superclass # => SystemCallError
puts e.class.superclass.superclass # => StandardError
end
ऊपर के उदाहरण में आप अपवाद देख सकते हैं Errno::ENOENT
पकड़ा जाता है जब उसके पैरेंट SystemCallError
बचाया जा रहा है।
अपवाद शृंखला में बहुत ऊपर की ओर बचाव करना
यह महत्वपूर्ण है कि अपवादों को अपवाद श्रृंखला में बहुत ऊपर तक नहीं बचाया जाए। जब आप ऐसा करते हैं, तो सभी उपवर्ग अपवाद भी पकड़े जाएंगे, जिससे बचाव ब्लॉक का कैप्चर बहुत सामान्य हो जाएगा।
यहां एक प्रोग्राम है जो प्रोग्राम को दिए गए तर्क के आधार पर एक कॉन्फ़िग फ़ाइल पढ़ता है।
# $ ruby example.rb config.yml
def config_file
ARGV.firs # Note the typo here, we meant `ARGV.first`.
end
begin
File.read config_file
rescue
puts "Couldn't read the config file"
end
त्रुटि संदेश कहता है कि यह कॉन्फ़िग फ़ाइल को नहीं पढ़ सका, लेकिन वास्तविक समस्या कोड में एक टाइपो थी।
begin
File.read config_file
rescue => e
puts e.inspect
end
#<NoMethodError: undefined method `firs' for []:Array>
डिफ़ॉल्ट अपवाद वर्ग एक begin .. rescue
. द्वारा पकड़ा गया ब्लॉक मानक त्रुटि है। यदि हम किसी विशिष्ट वर्ग में उत्तीर्ण नहीं होते हैं, तो रूबी StandardError और सभी उपवर्गित त्रुटियों से बचाव करेगी। NoMethodError इनमें से एक त्रुटि है।
एक विशिष्ट अपवाद वर्ग को बचाने से असंबंधित त्रुटियों को गलती से एक विफलता स्थिति का संकेत देने से रोकने में मदद मिलेगी। यह अधिक विशिष्ट कस्टम त्रुटि संदेशों की भी अनुमति देता है जो अंतिम उपयोगकर्ता के लिए अधिक सहायक होते हैं।
config_file = "config.yml"
begin
File.read config_file
rescue Errno::ENOENT => e
puts "File or directory #{config_file} doesn't exist."
rescue Errno::EACCES => e
puts "Can't read from #{config_file}. No permission."
end
बचाव अपवाद
अपवाद श्रृंखला में उच्च को बचाने के लिए यह अभी भी आकर्षक हो सकता है। किसी एप्लिकेशन द्वारा उठाई जा सकने वाली सभी त्रुटियों से बचाव उसे क्रैश होने से रोकेगा। (100% अपटाइम यहाँ हम आते हैं!) हालाँकि, यह बहुत सारी समस्याएँ पैदा कर सकता है।
रुबी में अपवाद वर्ग मुख्य अपवाद वर्ग है। अन्य सभी अपवाद इस वर्ग के उपवर्ग हैं; अगर अपवाद बचाया जाता है तो सभी त्रुटियां पकड़ी जाएंगी।
दो अपवाद जिन्हें अधिकांश एप्लिकेशन बचाव नहीं करना चाहेंगे, वे हैं SignalException और SystemExit।
<ब्लॉकक्वॉट>सिग्नल अपवाद का उपयोग तब किया जाता है जब कोई बाहरी स्रोत एप्लिकेशन को रोकने के लिए कह रहा हो। यह ऑपरेटिंग सिस्टम हो सकता है जब वह शट डाउन करना चाहता है, या एक सिस्टम एडमिनिस्ट्रेटर जो एप्लिकेशन को रोकना चाहता है। उदाहरण
<ब्लॉकक्वॉट>
SystemExit का उपयोग तब किया जाता है जब exit
रूबी आवेदन से बुलाया जा रहा है। जब यह उठाया जाता है तो डेवलपर चाहता है कि एप्लिकेशन बंद हो जाए। उदाहरण
यदि हम अपवाद को बचाते हैं और इन अपवादों को तब उठाया जाता है जब कोई एप्लिकेशन वर्तमान में चल रहा हो begin ... rescue ... end
ब्लॉक करें यह बाहर नहीं निकल सकता।
सामान्य परिस्थितियों में अपवाद को बचाना आम तौर पर एक बुरा विचार है। अपवाद को बचाते समय, आप SignalException और SystemExit को कार्य करने से रोकेंगे, लेकिन कुछ नाम रखने के लिए LoadError, SyntaxError और NoMemoryError भी। इसके बजाय अधिक विशिष्ट अपवादों को बचाना बेहतर है।
परीक्षणों में विफलता
जब अपवाद को बचाया जाता है, rescue Exception => e
. का उपयोग करके , आपके आवेदन के अलावा अन्य चीजें टूट सकती हैं। परीक्षण सूट वास्तव में कुछ त्रुटियों को छिपा सकता है।
मिनिटेस्ट और आरएसपीईसी में विफल होने वाले दावे आपको असफल दावे के बारे में सूचित करने के लिए अपवाद उठाएंगे, परीक्षण में असफल हो जाएंगे। जब वे ऐसा करते हैं, तो वे अपवाद से उप-वर्गीकृत अपने स्वयं के कस्टम अपवाद उठाते हैं।
यदि किसी परीक्षण या एप्लिकेशन कोड में अपवाद को बचाया जाता है, तो यह एक पुष्टि विफलता को शांत कर सकता है।
# RSpec example
def foo(bar)
bar.baz
rescue Exception => e
puts "This test should actually fail"
# Failure/Error: bar.baz
# <Double (anonymous)> received unexpected message :baz with (no args)
end
describe "#foo" do
it "hides an 'unexpected message' exception" do
bar = double(to_s: "")
foo(bar)
end
end
अपवादों की अपेक्षा करना
कुछ कोड अपवादों को बढ़ाने के लिए हैं। एक परीक्षण सूट में अपवाद को शांत करना संभव है ताकि परीक्षण उठाए जाने पर परीक्षण विफल न हो।
def foo
raise RuntimeError, "something went wrong"
end
foo rescue RuntimeError
हालांकि, यह परीक्षण नहीं करता है कि कोई अपवाद उठाया गया था या नहीं। जब अपवाद नहीं उठाया जाता है, तो आपका परीक्षण यह नहीं बता पाएगा कि व्यवहार अभी भी सही है या नहीं।
यह कहना संभव है कि अपवाद उठाया गया है, और यदि नहीं, तो कौन सा अपवाद था।
# expecting_exceptions_spec.rb
# RSpec example
def foo
raise NotImplementedError, "foo method not implemented"
end
describe "#foo" do
it "raises a RuntimeError" do
expect { foo }.to raise_error(RuntimeError)
end
end
1) #foo raises a RuntimeError
Failure/Error: expect { foo }.to raise_error(RuntimeError)
expected RuntimeError, got #<NotImplementedError: foo method not implemented> with backtrace:
# ./expecting_exceptions_spec.rb:4:in `foo'
# ./expecting_exceptions_spec.rb:9:in `block (3 levels) in <top (required)>'
# ./expecting_exceptions_spec.rb:9:in `block (2 levels) in <top (required)>'
# ./expecting_exceptions_spec.rb:9:in `block (2 levels) in <top (required)>'
अपवाद फिर से उठाएं
जब कोई बहुत अच्छा कारण हो तो किसी एप्लिकेशन को केवल अपवाद वर्ग के रूप में श्रृंखला में अपवादों को पकड़ना चाहिए। उदाहरण के लिए, जब कोड के ब्लॉक से बाहर निकलने से पहले कुछ सफाई शामिल होती है, जैसे अस्थायी फ़ाइलों को हटाना जिन्हें वास्तव में निकालने की आवश्यकता होती है।
जब आप अपवाद को पूरी तरह से बचाना चाहते हैं, तो एक सिफारिश, त्रुटि को संभालने के बाद इसे फिर से बढ़ाएं। इस तरह रूबी अपवाद हैंडलिंग बाद में प्रक्रिया के भाग्य का फैसला कर सकती है।
File.open("/tmp/my_app.status", "w") { |f| "running" }
begin
foo
rescue Exception => e
Appsignal.add_error e
File.open("/tmp/my_app.status", "w") { |f| "stopped" }
raise e
end
पता नहीं क्या बचाव करें?
जैसा कि पहले उल्लेख किया गया है, यह विशिष्ट होना अच्छा है कि किन त्रुटियों से बचाव करना है।
जब आप सुनिश्चित नहीं हैं कि कोई ऑपरेशन कौन से अपवाद उठा सकता है, तो StandardError को बचाना शुरू करने के लिए एक अच्छी जगह हो सकती है। अपने कोड को विभिन्न परिदृश्यों में चलाएँ और देखें कि यह क्या अपवाद उत्पन्न करता है।
begin
File.open('/tmp/appsignal.log', 'a') { |f| f.write "Starting AppSignal" }
rescue => e
puts e.inspect
end
#<Errno::EACCES: Permission denied @ rb_sysopen - /tmp/appsignal.log>
हर बार जब आप एक नया अपवाद देखते हैं, तो उन अपवादों या उसके प्रासंगिक मूल वर्ग के लिए विशिष्ट बचाव मामले जोड़ें। बहुत से अपवादों को बचाने के बजाय बचाव के बारे में विशिष्ट होना बेहतर है।
begin
file = '/tmp/appsignal.log'
File.open(file, 'a') { |f| f.write("AppSignal started!") }
rescue Errno::ENOENT => e
puts "File or directory #{file} doesn't exist."
rescue Errno::EACCES => e
puts "Cannot write to #{file}. No permissions."
end
# Or, using the parent error class
begin
file = '/tmp/appsignal.log'
File.open(file, 'a')
rescue SystemCallError => e
puts "Error while writing to file #{file}."
puts e
end
यह रूबी में अपवादों को संभालने पर हमारे प्राइमर को समाप्त करता है। यदि आप अधिक जानना चाहते हैं, या कोई विशिष्ट प्रश्न पूछना चाहते हैं, तो हमें @AppSignal पर बताएं। यदि आप इस बारे में बेहतर जानकारी प्राप्त करना चाहते हैं कि आपके ऐप में अपवाद कहां और कितनी बार उठाए गए हैं, तो ऐपसिग्नल को आज़माएं।