Rubyで例外が発生したときにローカル変数とインスタンス変数をログに記録する
簡単に再現できないバグがあったことはありますか?それは、人々があなたのアプリをしばらく使用したときにのみ起こるようです。そして、エラーメッセージとバックトレースは驚くほど役に立ちません。
例外が発生する直前のアプリの状態のスナップショットを撮ることができれば、このような場合に非常に便利です。たとえば、すべてのローカル変数とその値のリストを作成できる場合。ええと、できます-そしてそれはそれほど難しいことではありません!
この投稿では、例外時にローカルをキャプチャする方法を紹介します。しかし、最初に、私はあなたに警告する必要があります。 これらの手法はいずれも本番環境では使用しないでください。 ステージング、プレプロデュース、開発などで使用できます。本番環境では使用できません。私たちが使用する宝石は、かなり重い内省魔法に依存しており、せいぜいアプリの速度を低下させます。最悪の場合...誰が知っていますか?
binding_of_callerの紹介
binding_of_caller gemを使用すると、現在のスタックの任意のレベルのバインディングにアクセスできます。すっごく.....それは正確にはどういう意味ですか?
「スタック」は、現在「進行中」のメソッドのリストにすぎません。 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"
binding_of_caller gemを使用すると、現在の実行スタックの任意のレベルのバインディングにアクセスできます。たとえば、これを使用して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"
この時点で、おそらく2つの相反する感情を感じています。これは本当にクールなので、興奮。そして嫌悪感。これは、DHHと言うよりも早く依存関係の醜い混乱に退化する可能性があるためです。
binding_of_callerをマスターしたので、例外時にすべてのローカル変数をログに記録するのは簡単です。以下の例では、raiseメソッドをオーバーライドしています。私の新しい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
。
これはかなりクールなトリックです。ただし、特にアプリがステージング中であり、複数のユーザーが使用している場合は、ログファイルを確認するのがバグを修正するための最も便利な方法ではありません。また、維持しなければならないのはコードだけです。
Honeybadgerを使用してアプリのエラーを監視している場合は、ローカルを自動的にキャプチャできます。あなたがしなければならないのは、binding_of_callergemをGemfileに追加することだけです:
# Gemfile
group :development, :staging do
# Including this gem enables local variable capture via Honeybadger
gem "binding_of_caller"
...
end
これで、例外が発生するたびに、バックトレース、パラメータなどとともにすべてのローカルのレポートが表示されます。
-
Rubyで環境変数を使用する方法
環境変数はキーと値のペアであり、次のようになります。 KEY=VALUE これらの変数を使用して、コンピューター内のすべてのプログラム間で構成オプションを共有します。 そのため、それらがどのように機能するか、およびENVを使用してRubyプログラムからそれらにアクセスする方法を学ぶことが重要です。 特別な変数。 環境変数の例 : デフォルトのエディターの構成 宝石の場所をRubyに伝える(GEM_PATH / GEM_HOME ) APIキーを、ソース管理(git)にコミットせずにアプリケーションに渡す オペレーティングシステムがバイナリファイル(Windowsでは.exe)を検
-
Rubyのケースステートメントの多くの用途
if / elsifを使用する必要があるときはいつでも 代わりにRubyのcaseステートメントを使用することを検討できるステートメント。この投稿では、いくつかの異なるユースケースと、それが実際に内部でどのように機能するかを学びます。 注:他のプログラミング言語では、これはスイッチとして知られています。 ステートメント。 Rubyのcaseステートメントのコンポーネント: キーワード 説明 ケース ケースステートメントの定義を開始します。使用する変数を取得します。 いつ 一致する可能性のあるすべての条件は1つのwhenステートメントです。 その他 一致