DogStatsD を使った、メトリクスの送信

概要

このページでは、アプリケーションのメトリクスをDatadogに送信する方法について説明します。

Datadogにアプリケーションのメトリクスを送信することで、アプリケーションの状態、ユーザの行動、システムの状態などを関連付けて把握できるようになります。

メトリクスは、Datadog Agent にバンドルされているDogStatsD という負荷の少ないメトリクス集約サーバを介しDatadogに転送されています。DogStatsDの動作の詳細に関しては、“DogStatsDの解説”で理解を深めることができます。今すぐメトリクスを送信するためのコードを開発する必要のある場合は、このまま読み進めてください。

以下のチュートリアルでは、次のようなユースケースを取り上げます:

  • Webページのページビューを測定する方法
  • データベースのクエリ時間を測定する方法
  • 空きメモリの量を測定する方法

セットアップ

まず、ホストにDogStatsDがバンドルされているDatadog Agent(Ver. 3以降) をインストールし、動作していることを確認します。

次に、開発に使用するプログラミング言語に向けたクライアントライブラリーをインストール&セットアップします。

プログラム言語へモジュールをインストールします:
$ pip install datadog
アプリケーションへモジュールをインポートします:
from datadog import statsd

プログラム言語へモジュールをインストールします:
$ gem install dogstatsd-ruby
アプリケーションへモジュールをインポートするためにrequire行を追記します:
# Import the library
require 'datadog/statsd'

# Create a statsd client instance.
statsd = Datadog::Statsd.new

これで、ライブラリーが使えるように成りました。

このチュートリアルでは、PythonとRubyの例を掲載しています。他のプログラミング言語を利用する場合は、Librariesのページを参照してくだい。

メトリクスの命名規則

メトリクスを命名する際には、次のルールを守ってください:

  • 文字で始まる必要があります。
  • 半角英数字、アンダースコア、ピリオドを、使うことができができます。(他の文字はアンダースコアに変換されます)
  • 200文字を、超えることができません。 (ユーザインターフェースの観点からは、100文字以下が一般的です)
  • Unicode (全角文字)は、サポートしていません。
  • 空白(スペース)は、使用できません。

Datadog Agent によって転送されるメトリクスは、擬似階層ドット形式 (e.g. http.nginx.response_time)を使用しています。 ここで”疑似階層”と言っている理由は、階層的表記のメリットを生かし、メトリクスを検索する仕組みとして使ってはいるが、監視対象のシステムは階層構造である必要がないということです。 (例えば: “ねえ、ホストAとホストBが、’http.nginx.*‘というメトリクスを送信している。これらは、Webのフロントエンドのメトリクスだよね〜”)

カウンタ (Counters)

カウンタは、出来事の回数を数えるのに使用します。

例えば、Webページのビュー数をカウントしたい場合、Webアプリケーションのrender_page関数が実行された際に、web.page_viewsメトリクスが、増加するよう関数内にstatsd.increment()行を追記しカウント値を蓄積しています。

def render_page():
    """ Render a web page. """
    statsd.increment('web.page_views')
    return 'Hello World!'
def render_page()
  # Render a web page.
  statsd.increment('web.page_views')
  return 'Hello World!'
end

この1行のコードを追記するのみで、データのグラフ化を開始することができます。

次のグラフは、先の方法で収集したメトリクスをグラフ表示した例です:

  • 注) DogStatsDのデフォルト設定では10秒間隔でメトリクスをDatadogへ送信しています。カウンタは、この送信間隔の間の総カウント値を1秒間の数値に換算し、情報を送信しています。 従って、上のグラフのマーカでは、15:34分に35.33 view/秒という実際には考えられない数値を表示しています。

一定時間内の数値の増加や測定を目的とする場合は、ゲージを参照してください。

任意の数値でカウント値を増すこともできます。 例えばファイルのアップロードサービスで、処理されたバイト数をカウントしたいとします。 このようなケースでは、upload_file関数が実行される度に、file_service.bytes_uploadedというメトリクスを、ファイルサイズの値を使って増加させることができます:

def upload_file(file):
    statsd.increment('file_service.bytes_uploaded', file.size())
    save_file(file)
    return 'File uploaded!'
def upload_file(file)
  statsd.count('file_service.bytes_uploaded', file.size())
  save_file(file)
  return 'File uploaded!'
end

ゲージ (Gauges)

ゲージは、一定時間内の特定の値を測定します。 例えば、マシンの空きメモリの量を追跡した場合、空きメモリ量を調べる関数`get_free_memory()`を定期的に呼び出し、`statsd.gauge()`で、メトリクス`system.mem.free`の測定値`get_free_memory()`として処理します:
# Record the amount of free memory every ten seconds.
while True:
    statsd.gauge('system.mem.free', get_free_memory())
    time.sleep(10)
# Record the amount of free memory every ten seconds.
while true do
    statsd.gauge('system.mem.free', get_free_memory())
    sleep(10)
end

ヒストグラム (Histograms)

ヒストグラムは、データの統計的分布を測定します。

例えば、データベースへのクエリ時間待ち時間を計測したい場合、クエリの実行に掛かった時間を計測し、statsd.histogram()で、メトリクスdatabase.query.timeのヒストグラム値durationとして処理します:

# Track the run time of the database query.
start_time = time.time()
results = db.query()
duration = time.time() - start_time
statsd.histogram('database.query.time', duration)

# We can also use the `timed` decorator as a short-hand for timing functions.
@statsd.timed('database.query.time')
def get_data():
    return db.query()
start_time = Time.now
results = db.query()
duration = Time.now - start_time
statsd.histogram('database.query.time', duration)

# We can also use the `time` helper as a short-hand for timing blocks
# of code.
statsd.time('database.query.time') do
  return db.query()
end

ヒストグラム(Histogram)やタイマー(Timer)では、次のメトリクスを同時に生成されます:

  • database.query.time.count - 計測された回数
  • database.query.time.avg - 計測された値の平均時間
  • database.query.time.median - 計測された値の中央値
  • database.query.time.max - 計測された値の最大値
  • database.query.time.95percentile - 計測された値の95パーセンタイル値

これらのメトリクスにより、クエリが処理されるまでの時間のばらつきを把握すことができます。 medianをグラフ化することにより、クエリが処理されるまでの一般的な時間(中央値)を把握することができます。 又、95percentileをグラフ化することにより、異常値を取り除いた最大クエリ処理時間を把握することができます。

この例では、「クエリ処理時間の容認範囲は、1秒以内」と仮定し話を進めます。クエリ処理時間の中央値(紫色線)は、概ね100ミリ秒以下を示し、容認範囲を推移しています。 しかし残念ながら、95パーセンタイル値(青色線)では、放置することのできない長時間クエリがスパイク状に発生しているのが確認でき、長い時には3秒という値になっています。

この2本の線から、ほとんどのクエリは順調に処理されているが、時折発生する長処理時間のクエリは深刻であることが推測できます。 なんだかの対策を施し、95パーセンタイル値が中央値と近接して推移するようにできたら、クエリは期待通りに処理されるように改善されたことになり、グラフの変化からその対策は有効であったことが確認することができます。

ヒストグラムは、任意の型の値の分布を測定するために用いることもできます。 (例えば、"アップロード操作で受け付けたファイルのサイズ"や"試験の点数"のような値です。)

セット (Sets)

セットは、グループ内のユニークな要素をカウントするために使用します。

Webサイトなどの個別訪問者数を追跡する場合は、セットが最適です。

def login(self, user_id):
    # Log the user in ...
    statsd.set('users.uniques', user_id)
def login(self, user_id)
    # Log the user in ...
    statsd.set('users.uniques', user_id)
end

タグ (Tags)

タグは、メトリクスの分類に広がりを持たせるための方法です。ダッシュボード上のグラフ表示では、これらのタグの擬似階層構造を使い、メトリクスを抽出、分類、集約、比較し、システムの状況を把握しやすい表示を作っていきます。

例えば、2種類のアルゴリズムの性能を測定したい場合、 メトリクスalgorithm.run_timeにタグとしてtags=['key:value syntax']をオプション指定し、計測結果を送信します:

@statsd.timed('algorithm.run_time', tags=['algorithm:one'])
def algorithm_one():
    # Do fancy things here ...

@statsd.timed('algorithm.run_time', tags=['algorithm:two'])
def algorithm_two():
    # Do fancy things here ...
def algorithm_one()
  statsd.timed('algorithm.run_time', :tags => ['algorithm:one']) do
    # Do fancy things here ...
  end
end

def algorithm_two()
  statsd.timed('algorithm.run_time', :tags => ['algorithm:two']) do
    # Do different fancy things here ...
  end
end

ダッシュボード上にグラフ表示する際、メトリクス名だけでもメトリクス合計や平均を計算したり、最小値/最大値を表示したりすることができます。タグを使うつと更に高度な集計ができるようになります。同じ名前のメトリクスでも、タグで設定した’key’文字列を基に、’value syntax’にセットされた値毎に分類することができます。

例えば、次のようなクエリを実行することができます:

avg:algorithm.run_time{*} by {algorithm}

このクエリでは、algorithm.run_timeメトリクスを全てのデータから検索し、タグ’key’文字列のalgorithmの’value syntax’文字列ごとに分類集計し、平均を算出します。

Datadogのバックエンドでは、ホスト、メトリクス, タグの組み合わせごとにシリーズ(時系列データ)を保存しています。 従って、無限にタグづけされたメトリクスをサポートすることができません。 ユーザIDやタイムスタンプなど、無限に増殖する['key':'value syntax']の付与は止めてください。

それぞれのメトリクスにタグを付与する際には、1000種類を超えないように注意してください。

タグを付与するさに注意事項は:

  • タグは、文字で開始する必要があります。
  • 2文字目からは、英数字、アンダースコア、マイナス、コロン、ピリオドやスラッシュが使用できます。
  • 使用が許可されていない文字は全てアンダースコアに変換されます。
  • タグには、最大200文字で、Unicodeをサポートすることができます。
  • タグに使用した文字は、全て小文字に変換されます。

タグに備わっているオプションの機能を利用するためには、[‘key’:‘vale syntax’]構文を利用してください。メトリクスで一般的に使用されるタグの’key’文字列としては、env, instance, name, roleが考えられます。

注: device, host, sourceの’key’文字列は、特別な文字列として取り扱われるために、標準的な方法では設定できないようになっていますので注意してください。

特殊文字を含むタグの設定方法に関しては、次のドキュメントを参照してください:

サンプルレート (Sample Rates)

DogStatsDがメトリクスデータを生成するための基データ(サンプルポイント)は、UDPを介しDogStatsDサーバに送信されます。 この基データの送信する処理は、パフォーマンスを重視している環境において、不要なオーバヘッド負荷になる可能性があります。 この問題を回避するためにDogStatsDでは、サンプルレートを調整できるようになっています。 sample_rate=をオプションに追記することにより基データを制限することができ、制限し送信しなかった基データは、自動的に補われるよなっています。

次のコードは、基データ発生の1/2の機会に、データポイントを送信します:

while True:
  do_something_intense()
  statsd.increment('loop.count', sample_rate=0.5)
while true do
  do_something_intense()
  statsd.increment('loop.count', :sample_rate => 0.5)
end

他の送信方法

Datagogにメトリクスを送信するには、Datadog Agent にバンドルされているDogStatsD サーバを使用する方法が最も賢明で簡単な方法です。 しかし、それが唯一の方法ではありません。次の方法を使ってもDatadogにメトリクスを送信することができます。

  • DatadogのHTTP APIを使って直接送信する方法
  • codahaleのJava用 metrics ライブラリーと(Vistar Mediaが寄贈してくれた)metrics-datadogを使って送信する方法

カスタムメトリクスの確認方法

カスタムメトリクスを検索する最も簡単な方法は、メトリクスエクスプローラを使用することです。 Datadogにloginした後、上部のナビゲーションバーにあるMetricsよりExplorを選択します。

メトリクスエクスプローラのページが表示されたら、”Graph:“の欄にカスタムメトリクス名を入力します。 入力時には、自動補完が行われます。 自動補完がされない場合は、Datadogがそのメトリクスを数時間に渡って受信できていない可能性があります。

“Over:“欄を使うとメトリクスをフィルタリングすることもできます。 更に”One graph per:“では、メトリクスをタグクループごとにグラフ化することもできます。

メトリクスエクスプローラの項目に入力し表示されたグラフは、自動的に保存されません。 メトリクスエクスプローラの操作によって表示されたグラフを保存する必要がある場合は、メニュー左下の”A new dashboard”か”An existing dashboard”をクリックし手動で保存してください。