Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

なぜMackerelのPromQLでは識別子にドットを使えるのか

arthur-1 Mackerel Advent Calendar 2023ラソン18日目の記事です。明日はいよいよMackerel Meetup #15です!まだ間に合いますのでぜひお越しくださいね。

Mackerelのラベル対応メトリックはPromQLで自在に引ける

Mackerelのラベル対応メトリック機能(現在はプライベートベータ版)を用いると、OpenTelemetry Protocolで投稿したメトリックをPromQLを利用して自在に検索・集計・計算してグラフとして描画することが可能です。PromQLとはOSSの監視アプリケーションであるPrometheusで使われているメトリックのためのクエリ言語です。

最もよく使われるVector Selector記法を用いると、以下の画像のようにラベル名とラベル値を指定してメトリックを絞り込むことができます。

他にも、by修飾子を付与した集約演算子を用いて、単なる集計だけでなくラベルの値ごとの集計ができたり、

メトリックや数量を四則演算できたりと、できることがたくさんあります。

MackerelのPromQLではドットが使える

Prometheusを使ったことがある人はお気づきかもしれませんが、MackerelのPromQLでは、識別子(メトリック名やラベル名)に.を用いることができます。

対して、Prometheusではメトリック名やラベル名に.は使えません。以下の記事の通り、OpenTelemetry MetricsをPrometheusにexportする際にはメトリック名に含まれる.などの文字を_に変換して扱う必要があります。

opentelemetry.io

MackerelのPromQLでドットが使えるのはなぜなのでしょうか?

実装としての理由

技術的な理由を述べると、Prometheusのライブラリをforkし改修しているからです。

以下のようなisAlphaNumericDot関数を作った上で、isAlphaNumeric関数を呼んでいる箇所を置き換えました。

// isAlphaNumeric reports whether r is an alphabetic, digit, underscore, or dot.
func isAlphaNumericDot(r rune) bool {
    return isAlphaNumeric(r) || r == '.'
}

置き換えた場所はいくつかありますが、例を挙げておきます。

promql/parser/lex.go#L723

  func lexIdentifier(l *Lexer) stateFn {
-     for isAlphaNumeric(l.next()) {
+     for isAlphaNumericDot(l.next()) {
          // absorb
      }
      l.backup()
      l.emit(IDENTIFIER)
      return lexStatements
  }

promql/parser/lex.go#L991

  func isLabel(s string) bool {
      if len(s) == 0 || !isAlpha(rune(s[0])) {
          return false
      }
      for _, c := range s[1:] {
-         if !isAlphaNumeric(c) {
+         if !isAlphaNumericDot(c) {
              return false
          }
      }
      return true
  }

テストケースの追加を除くと、差分としては10数行で収まっているはずです。

仕様としての理由

では、なぜforkして改修してまでドットが使えることにこだわったのでしょうか。

答えは、OpenTelemetryのメトリック名や属性名ではドットが使用可能な上、また名前空間を区切る目的でドットを積極的に使う慣習があったことです。例えば、Kubernetesのメトリックをデフォルトの設定のままexportすると、メトリック名や属性名にk8s.pod.phaseのようにドットが含まれています。

そもそも、PromQLを選択した理由の一つは、すでにラベル付きのメトリックのクエリ言語として成熟していて、これを活用することでリリースまでの速度を短縮できるからでした。

我々はPromQLに対応したいのではなく、OpenTelemetryに対応したいのです。OpenTelemetryの規格に沿った標準のメトリックを、素のOTLP exporterでexportできない *1 のは不便だと考え、ドットを使えるように手を入れることにしました。もしその手段さえ取れなかったら、他の言語を選択するか、あるいは自作していたかもしれません。

もっとも、OpenTelemetryの仕様ではメトリック名に-も利用可能ですが、こちらはMackerelのPromQLでも引き続き扱えません。これは減算の算術演算子と衝突してしまうからです。しかし、利用可能とはいえ積極的に使われている様子は見られないため、こちらの対応は見送りました。

forkしたライブラリの継続的な運用が難しいことなどから、実は開発チームの中でドット対応は切っても良いのではないかという議論も起こっていました。私がその時粘っていなかったらドットは使えなくなっていたかもしれませんし、今後も状況によっては対応しなくなるかもしれません。

ラベル付きメトリック機能のプライベートベータ版のユーザーを募集しています

冒頭で述べた通り、ラベル付きメトリック機能は現在プライベートベータ版となっています。OpenTelemetry MetricsをMackerelにエクスポートしてみたい人、PromQLによるメトリックのクエリを試してみたい人は、ぜひ以下のフォームからお申し込みください。

docs.google.com

*1:厳密にはexportしようものならできるがメトリックを引くことができない