Rubyのコードを計測

GitLab Performance Monitoringでは、メソッドとRubyコードのカスタムブロックの両方をインスツルメンテーションすることができます。 メソッドインスツルメンテーションはインスツルメンテーションの主要な形式であり、ブロックベースのインスツルメンテーションはメソッド内のコードの特定の領域をドリルダウンしたい場合にのみ使われます。

製品の使用パターンを追跡する場合は、テレメトリーを参照してください。

計装方法

メソッドのインスツルメンテーションは、Gitlab::Metrics::Instrumentationモジュールを使って行います。 このモジュールには、コードのインスツルメンテーションに使えるいくつかのメソッドが用意されています:

  • instrument_method単一のクラスメソッドを使用します。
  • instrument_instance_methodインスタンスメソッドを1つ生成します。
  • instrument_class_hierarchyクラスが与えられると、このメソッドはすべてのサブクラス (クラス・メソッドとインスタンス・メソッドの両方) を再帰的にインスツルメントします。
  • instrument_methods: モジュールのすべての公開および非公開クラスメソッドを計測します。
  • instrument_instance_methods: モジュールのすべての公開および非公開インスタンスメソッドを計測します。

完全なGitlab::Metrics::Instrumentation名前空間を入力する必要をなくすには、configure クラス・メソッドを使います。 このメソッドは、Gitlab::Metrics::Instrumentation を引数として渡しながら、与えられたブロックを返すだけです。 例を挙げましょう:

Gitlab::Metrics::Instrumentation.configure do |conf|
  conf.instrument_method(Foo, :bar)
  conf.instrument_method(Foo, :baz)
end

一般的には、この方法を使う方が、様々な計装メソッドを直接呼び出すよりも好ましい。

メソッドのインスツルメンテーションは、イニシャライザーconfig/initializers/zz_metrics.rbで追加する必要があります。

使用例

単一メソッドのインストゥルメント:

Gitlab::Metrics::Instrumentation.configure do |conf|
  conf.instrument_method(User, :find_by)
end

クラス階層全体のインストルメンテーション

Gitlab::Metrics::Instrumentation.configure do |conf|
  conf.instrument_class_hierarchy(ActiveRecord::Base)
end

すべての公開クラス・メソッドのインスツルメンテーション:

Gitlab::Metrics::Instrumentation.configure do |conf|
  conf.instrument_methods(User)
end

インスツルメンテッド・メソッドのチェック

メソッドがインスツルメンテッド化されているかどうかを確認する最も簡単な方法は、そのソースの場所を確認することです。 例えば、以下のようになります:

method = Banzai::Renderer.method(:render)

method.source_location

ソースの位置がlib/gitlab/metrics/instrumentation.rb を指していれば、そのメソッドがインスツルメンテーションされていることがわかります。

Pryを使用している場合、$ コマンドを使用してメソッドのソースコード(ソースの場所とともに)を表示することができます。これは上記のRubyコードを実行するよりも簡単です。 上記のスニペットの場合、以下を実行します:

  • $ Banzai::Renderer.render

これは次のような内容を出力します:

From: /path/to/your/gitlab/lib/gitlab/metrics/instrumentation.rb @ line 148:
Owner: #<Module:0x0055f0865c6d50>
Visibility: public
Number of lines: 21

def #{name}(#{args_signature})
  if trans = Gitlab::Metrics::Instrumentation.transaction
    trans.measure_method(#{label.inspect}) { super }
  else
    super
  end
end

Rubyブロックのインスツルメンテーション

Rubyコードのブロックを計測するには、Gitlab::Metrics.measure を呼び出してブロックを渡します。 例えば、次のようにします:

Gitlab::Metrics.measure(:foo) do
  ...
end

ブロックは実行され、実行時間は現在実行中のトランザクションのフィールドのセットとして保存されます。 トランザクションが存在しない場合、ブロックは何も測定せずに終了します。

1つのブロックに対して3つの値が測定されます:

  • NAME_real_timeに格納されている経過時間。
  • NAME_cpu_timeに格納されているCPU経過時間。
  • NAME_call_countに格納されているコール数。

実際のタイミングとCPUのタイミングはどちらもミリ秒単位です。

同じブロックを複数回呼び出すと、最終的な値は個々の値の合計になります。 たとえば次のコードです:

3.times do
  Gitlab::Metrics.measure(:sleep) do
    sleep 1
  end
end

ここでsleep_real_time の最終値は1ではなく 3となります。

カスタムイベントのトラッキング

GitLab パフォーマンスモニタリングはコードのインスツルメンテーションだけでなく、カスタムイベントのトラッキングもサポートしています。 これは主に、Git プッシュ数やリポジトリインポートなどのビジネスメトリクスのトラッキングに使うことを想定しています。

カスタムイベントをトラッキングするには、Gitlab::Metrics.add_event を呼び出し、イベント名とカスタムタグ(オプション)を渡します:

Gitlab::Metrics.add_event(:user_login, email: current_user.email)

イベント名は、push_repositoryremove_branchのような動詞にしましょう。