कभी-कभी यह समझना वास्तव में मुश्किल हो सकता है कि एक्सेप्शन के साथ क्या हो रहा है - विशेष रूप से बड़े ऐप्स में। कल्पना करें कि आप किसी मौजूदा प्रोजेक्ट के अंदर कुछ कोड पर काम कर रहे हैं। आप एक एक्सेप्शन उठाते हैं, फिर कुछ अजीब होता है। शायद एक्सेप्शन निगल लिया जाता है। शायद एक एनवायरनमेंट वेरिएबल बदल जाता है। शायद आपका एक्सेप्शन दूसरे एक्सेप्शन में लपेट दिया जाता है।
मैं आपको TracePoints का उपयोग करके अपने ऐप में एक्सेप्शन के बारे में थोड़ी अधिक जानकारी प्राप्त करने का एक सरल तरीका दिखाने जा रहा हूँ - भले ही उन एक्सेप्शन को निगल लिया गया हो।
एक सुविधाजनक उदाहरण
रेल्स में कंट्रोलर और व्यू के बीच की सीमा एक ऐसा स्थान है जहां एक्सेप्शन तर्क को धता बताते प्रतीत होते हैं। इसे स्वयं देखना आसान है। बस एक व्यू में एक एक्सेप्शन उठाएं और कंट्रोलर में इसे रेस्क्यू करने का प्रयास करें। आप पाएंगे कि आप कंट्रोलर से टेम्पलेट एरर को रेस्क्यू नहीं कर सकते!
# pages_controller.rb def index render rescue # this will never run logger.debug "someone raised the roof" end # index.haml - raise "the roof"
WTF!?! मुझे लगा कि मैंने इसे रेस्क्यू कर लिया!
यह स्पष्ट है कि कुछ मुश्किल चल रहा है। आइए देखें कि क्या हम यह पता लगा सकते हैं कि यह क्या है।
TracePoint के साथ सभी एक्सेप्शन लॉग करना
TracePoints एक बहुत शक्तिशाली इंट्रोस्पेक्शन टूल हैं जो रूबी 2.0 से मौजूद हैं। वे आपको रनटाइम इवेंट की एक विस्तृत विविधता के लिए कॉलबैक परिभाषित करने की अनुमति देते हैं। उदाहरण के लिए, आपको जब भी कोई क्लास परिभाषित की जाती है, जब भी कोई मेथड कॉल किया जाता है, या जब भी कोई एक्सेप्शन उठाया जाता है, तो सूचित किया जा सकता है। और भी अधिक इवेंट के लिए TracePoint दस्तावेज़ देखें।
आइए एक TracePoint जोड़कर शुरू करें जिसे हर बार एक्सेप्शन उठाए जाने पर कॉल किया जाता है और उसका सारांश लॉग में लिखता है।
class PagesController < ApplicationController def index TracePoint.new(:raise) do |tp| # tp.raised_exeption contains the actual exception object that was raised! logger.debug "#{tp.raised_exception.object_id}: #{tp.raised_exception.class} #{tp.raised_exception.message} ".yellow + tp.raised_exception.backtrace[0].sub(Rails.root.to_s, "").blue end.enable do render end end end
यदि आप yellow और blue मेथड के बारे में उत्सुक हैं, तो मैं colorize जेम का उपयोग कर रहा हूँ। यह आउटपुट में ANSI कलर कोड जोड़ता है।
अब जब मैं जाकर अपना पेज रीफ्रेश करता हूँ, तो मेरे लॉग नीचे स्क्रीनशॉट की तरह दिखते हैं। एक दिलचस्प बात जो आप देख सकते हैं वह यह है कि दो अलग-अलग एक्सेप्शन हैं, और उनमें से प्रत्येक को दो बार उठाया गया है। प्रत्येक पंक्ति की शुरुआत में वह लंबी संख्या एक्सेप्शन की ऑब्जेक्ट आईडी है। इसी तरह हम जानते हैं कि चार नहीं, बल्कि दो एक्सेप्शन ऑब्जेक्ट हैं।
यह लॉग रेंडरिंग प्रक्रिया में raise के हर उपयोग को दिखाता है
किस मेथड ने कौन सा raise कारण बनाया?
"raise" इवेंट की एक सूची होना काफी उपयोगी है। लेकिन यह और भी बेहतर होगा अगर हमें कुछ अंदाजा हो कि कौन से मेथड प्रत्येक raise का कारण बने। एक बार फिर, TracePoint बचाव के लिए आता है।
TracePoint हमें एक हैंडलर जोड़ने देता है जिसे हर बार मेथड रिटर्न होने पर कॉल किया जाता है। यह "raise" इवेंट जितना ही सरल है। नीचे दिए गए उदाहरण में हम हर मेथड रिटर्न को लॉग कर रहे हैं:
TracePoint.trace(:return) do |tp| logger.debug [tp.method_id, tp.lineno, tp.path.sub(Rails.root.to_s, "")].join(" : ").green end
एक समस्या है हालांकि। यदि आप इस कोड को अपने रेल्स ऐप में जोड़ते हैं, तो आप पाएंगे कि आपका ऐप अनुरोधों का जवाब देना बंद कर देता है। सबसे सरल रेल्स अनुरोध में इतने सारे मेथड कॉल होते हैं कि सर्वर उन सभी को लॉग में लिखने से पहले टाइम आउट हो जाता है।
चूंकि हम वास्तव में केवल उन मेथड कॉल में रुचि रखते हैं जिनके कारण एक्सेप्शन हुए, आइए अपने कोड को संशोधित करें ताकि प्रत्येक एक्सेप्शन के बाद पहले दो "रिटर्न" इवेंट आउटपुट हों।
class PagesController < ApplicationController def index counter = 0 return_trace = TracePoint.trace(:return) do |tp| logger.debug "\t" + [tp.method_id, tp.lineno, tp.path.sub(Rails.root.to_s, "")].join(" : ").green if (counter += 1) > 3 return_trace.disable counter = 0 end end return_trace.disable # disable the tracepoint by default TracePoint.new(:raise) do |tp| logger.debug "#{tp.raised_exception.object_id}: #{tp.raised_exception.class} #{tp.raised_exception.message} ".yellow + tp.raised_exception.backtrace[0].sub(Rails.root.to_s, "").blue # The "raise" enables the "return" tracepoint return_trace.enable end.enable do render end end end
जब मैं अपना ब्राउज़र रीफ्रेश करता हूँ, तो मुझे लॉग में निम्नलिखित पंक्तियाँ जुड़ी हुई दिखाई देती हैं:
प्रत्येक "raise" इवेंट को उसके कारण बने मेथड के ऊपर दिखाया गया है
क्योंकि हम "रिटर्न" TracePoint को केवल तभी सक्षम करते हैं जब कोई एक्सेप्शन उठाया जाता है, पहला "रिटर्न" इवेंट उस मेथड से होने वाला है जिसने एक्सेप्शन उठाया।
हम इस जानकारी का उपयोग अपने रहस्य को सुलझाने के लिए कर सकते हैं। हमारा मूल RuntimeError handle_render_error मेथड द्वारा template.rb की लाइन 310 पर ActionView::Template::Error में परिवर्तित किया जा रहा है।
इस तकनीक के बारे में अच्छी बात यह है कि इसका रेल्स से कोई लेना-देना नहीं है। आप इसका उपयोग किसी भी समय कर सकते हैं जब आपको अधिक विस्तार से समझने की आवश्यकता हो कि हुड के तहत कौन से एक्सेप्शन उठाए और पकड़े जा रहे हैं।