क्या आपके पास कभी कोई बग है जिसे आप आसानी से पुन:उत्पन्न नहीं कर सकते हैं? ऐसा तभी होता है जब लोगों ने आपके ऐप को कुछ समय के लिए इस्तेमाल किया हो। और त्रुटि संदेश और बैकट्रेस आश्चर्यजनक रूप से अनुपयोगी हैं।
यह ऐसा समय है जब आप अपवाद होने से ठीक पहले ऐप की स्थिति का एक स्नैपशॉट ले सकते हैं तो यह वास्तव में आसान होगा। यदि आपके पास, उदाहरण के लिए, सभी स्थानीय चर और उनके मूल्यों की एक सूची हो सकती है। ठीक है, आप कर सकते हैं - और यह इतना कठिन भी नहीं है!
इस पोस्ट में मैं आपको दिखाऊंगा कि अपवाद के समय स्थानीय लोगों को कैसे पकड़ा जाए। लेकिन पहले, मुझे आपको चेतावनी देने की जरूरत है। उत्पादन में इनमें से किसी भी तकनीक का उपयोग नहीं किया जाना चाहिए। आप उन्हें स्टेजिंग, प्रीप्रोड, डेवलपमेंट इत्यादि में इस्तेमाल कर सकते हैं। सिर्फ प्रोडक्शन नहीं। हम जिन रत्नों का उपयोग करेंगे, वे कुछ बहुत भारी आत्मनिरीक्षण जादू पर निर्भर करते हैं, जो आपके ऐप को धीमा कर देगा। कम से कम...कौन जानता है?
पेश है बाइंडिंग_ऑफ़_कॉलर
बाइंडिंग_ऑफ_कॉलर रत्न आपको मौजूदा स्टैक के किसी भी स्तर के लिए बाइंडिंग एक्सेस करने देता है। सू ..... इसका वास्तव में क्या मतलब है?
"स्टैक" वर्तमान में "प्रगति में" विधियों की एक सूची है। आप caller
. का उपयोग कर सकते हैं वर्तमान स्टैक की जांच करने की विधि। यहां एक सरल उदाहरण दिया गया है:
def a
puts caller.inspect # ["caller.rb:20:in `<main>'"]
b()
end
def b
puts caller.inspect # ["caller.rb:4:in `a'", "caller.rb:20:in `<main>'"]
c()
end
def c
puts caller.inspect # ["caller.rb:11:in `b'", "caller.rb:4:in `a'", "caller.rb:20:in `<main>'"]
end
a()
बाइंडिंग वर्तमान निष्पादन संदर्भ का एक स्नैपशॉट है। नीचे दिए गए उदाहरण में, मैं एक विधि के बंधन को कैप्चर करता हूं, फिर इसका उपयोग विधि के स्थानीय चरों तक पहुंचने के लिए करता हूं।
def get_binding
a = "marco"
b = "polo"
return binding
end
my_binding = get_binding
puts my_binding.local_variable_get(:a) # "marco"
puts my_binding.local_variable_get(:b) # "polo"
बाइंडिंग_ऑफ_कॉलर रत्न आपको मौजूदा निष्पादन स्टैक के किसी भी स्तर के लिए बाइंडिंग तक पहुंच प्रदान करता है। उदाहरण के लिए, मैं इसका उपयोग c
. को अनुमति देने के लिए कर सकता था a
. तक विधि पहुंच विधि के स्थानीय चर।
require "rubygems"
require "binding_of_caller"
def a
fruit = "orange"
b()
end
def b
fruit = "apple"
c()
end
def c
fruit = "pear"
# Get the binding "two levels up" and ask it for its local variable "fruit"
puts binding.of_caller(2).local_variable_get(:fruit)
end
a() # prints "orange"
इस समय, आप शायद दो परस्पर विरोधी भावनाओं को महसूस कर रहे हैं। उत्साह, क्योंकि यह वास्तव में अच्छा है। और घृणा, क्योंकि यह निर्भरता की एक बदसूरत गड़बड़ी में तेजी से पतित हो सकता है, जितना कि आप डीएचएच कह सकते हैं।
अपवाद के समय स्थानीय लोगों को लॉग करना
अब जब हमने बाइंडिंग_ऑफ_कॉलर में महारत हासिल कर ली है, तो अपवाद के समय सभी स्थानीय चरों को लॉग करना केक का एक टुकड़ा है। नीचे दिए गए उदाहरण में मैं raise विधि को ओवरराइड कर रहा हूं। मेरी नई वृद्धि विधि जो भी विधि कहलाती है उसका बंधन प्राप्त करती है। फिर यह सभी स्थानीय लोगों के माध्यम से पुनरावृति करता है और उन्हें प्रिंट करता है।
require "rubygems"
require "binding_of_caller"
module LogLocalsOnRaise
def raise(*args)
b = binding.of_caller(1)
b.eval("local_variables").each do |k|
puts "Local variable #{ k }: #{ b.local_variable_get(k) }"
end
super
end
end
class Object
include LogLocalsOnRaise
end
def buggy
s = "hello world"
raise RuntimeError
end
buggy()
यहां बताया गया है कि यह क्रिया में कैसा दिखता है:
व्यायाम:लॉग इंस्टेंस वैरिएबल
मैं इसे आपके लिए स्थानीय लोगों के साथ इंस्टेंस वेरिएबल लॉग करने के लिए एक अभ्यास के रूप में छोड़ दूँगा। यहां एक संकेत दिया गया है:आप my_binding.eval("instance_variables")
का उपयोग कर सकते हैं और my_binding.instance_variable_get
ठीक उसी तरह जैसे आप my_binding.eval("local_variables")
का उपयोग करेंगे और my_binding.instance_variable_get
।
आसान तरीका
यह काफी मस्त ट्रिक है। लेकिन लॉग फ़ाइलों के इर्द-गिर्द टटोलना बग्स को ठीक करने का सबसे सुविधाजनक तरीका नहीं है, खासकर यदि आपका ऐप स्टेजिंग पर है और आपके पास इसका उपयोग करने वाले कई लोग हैं। साथ ही, यह और कोड है जिसे आपको बनाए रखना है।
यदि आप त्रुटियों के लिए अपने ऐप की निगरानी के लिए हनीबैगर का उपयोग करते हैं, तो हम स्थानीय लोगों को स्वचालित रूप से पकड़ सकते हैं। आपको बस अपने जेमफाइल में बाइंडिंग_ऑफ_कॉलर रत्न जोड़ना है:
# Gemfile
group :development, :staging do
# Including this gem enables local variable capture via Honeybadger
gem "binding_of_caller"
...
end
अब, जब भी कोई अपवाद होता है, तो आपको बैकट्रेस, पैराम्स आदि के साथ सभी स्थानीय लोगों की रिपोर्ट मिल जाएगी।