本記事はEnhanced string parsing for better, more efficient NRQL queriesの抄訳記事です。

業界全般において、ログを解析し理解することはしばしばチャレンジとなり得ます。特に、ログには半構造化された巨大なテキストが含まれることが多いため、New Relicクエリ言語(NRQL)で処理するのは困難でした。以前の投稿で、New Relic上で正規表現をキャプチャし、URLから値を抽出するNRQLのクエリ例を概説しました。今回のブログでは、NRQL変数、正規表現マルチキャプチャなど、NRQLの新しい機能群を使用してデータをより迅速、かつ効率的に分析する方法をご紹介します。これらのNRQLの機能向上によって、あなたのアプリケーションの稼働状況に洞察をより迅速に、より簡潔に得ることができます。

複雑なログの解析

まず、ログデータから分析することができた可能性のある過去の問題を見てみましょう。大半の複雑なテキストデータフィールドと同様、ログデータには、長い文字列から解析する必要がある重要な情報が含まれていることがよくあります。ここでひとつ、例をあげて解説しましょう。

FROM Log SELECT message

この例からいくつかの行を詳しく見てみましょう:

ts=2022-10-04T17:23:52.685261818Z caller=middlewares.go:75 method=GetUsers id=57a98d98e4b00679b4a830b2 result=1 took=375.146 µs
ts=2022-10-04T17:23:52.685261818Z caller=middlewares.go:75 id=57a98d98e4b00679b4a830b2 method=Get result=1 took=2.146 ms

このサービスから取得できている唯一のデータが上記のログデータであり、エンドポイントの各methodの応答時間であるtookの99パーセンタイルを計算する必要があると仮定しましょう。残念ながら、これらのログ行のtookの値にはμsmsの両方があります。従来、これらのログを標準化された時間単位で抽出するのは困難でした。生産性を高めるためのNew Relicの新機能(この記事の後半で学習します)が追加される前は、次のような恐るべきクエリが必要になることがありました。

FROM Log
SELECT percentile((
     numeric(capture(message, r'.*took=(?P<microTime>\d*\.?\d*) µs')) / 1000
     OR
     numeric(capture(message, r'.*took=(?P<milliTime>\d*\.?\d*) ms'))
   ),
   99) AS 'duration 99th'
FACET capture(message, r'.*method=(?P<method>[[:alpha:]]+) id=.*')

これは複雑なクエリですが、機能はします。このクエリからは次のような結果が得られます:

このクエリは、判読するのが難しいうえ、3つの正規表現を必要とし、解しがたいクエリ機能を使用しています。前提となる状況を知らない人にとって、このクエリを解釈するのは非常に難しいかもしれません。

NRQL変数、正規表現マルチキャプチャ、アンカーパースを使用して、このようなクエリを大幅に簡素化することが可能になりました。このクエリをステップごとに分解し、それぞれの過程で新しい機能を使用して問題を大幅に簡素化する方法を見てみましょう。

NRQL変数による複雑さの軽減

 

上記で使用している複雑なクエリの改善を開始するにあたり、最初の新機能であるNRQL変数を見てみましょう。NRQL変数を使用すると、非集計関数または属性(行単位)から計算された値を、クエリ内の他の場所で参照できる識別名に割り当てることができます。NRQL変数は、WITHという新しい句を使用します。この句は、わかりやすくするためにFROM句の後に使用するのが最も適しています。WITHは、割り当てにASを使用します(=を使用するプログラミング言語とは異なります)。

...WITH round(attribute, 10) AS roundedAttribute ...

先程の恐るべきクエリがNRQL変数では次のように記述されます:

FROM Log 
WITH numeric(capture(message, r'.*took=(?P<microTime>\d*\.?\d*) µs')) AS microTime,
   numeric(capture(message, r'.*took=(?P<milliTime>\d*\.?\d*) ms')) AS milliTime,
   microTime / 1000 OR milliTime AS duration SELECT percentile(duration, 99) AS 'duration 99th'
FACET capture(message, r'.*method=(?P<method>[[:alpha:]]+) id=.*')

NRQL変数では、計算が変数 microTime および milliTimeとして明確に識別され、クエリが読みやすくなりました。これらの変数は次に、標準化された単一のduration変数に割り当てられ、クエリの中で使用できるようになりました。もしdurationが1ミリ秒より長いログを見たい場合は、...WHERE duration > 1...を追加するだけで見ることができます。変数なしでそれを行うことがどれほど扱いにくいか想像してみてください!

重要なメモ

  • duration(またはその他の変数)が既にログイベントの属性であった場合、オブジェクト指向言語の関数オーバーロードと同様、束縛変数が優先されます。
  • 変数にpercentile関数のような集計関数を割り当てることはできません。変数に割り当てることができるのは、行単位の関数のみです。大雑把に言うと、その関数を使用して複数の属性を含み、facetでグルーピングされていないのテーブルのビューを作成できる場合、それは行単位の関数だと言えます。

正規表現のマルチキャプチャとIf関数によるパフォーマンスとシンプルさの向上

引き続きクエリをさらに洗練させましょう。NRQL変数はクエリを読みやすく解釈しやすいものにしましたが、messageフィールドを読み取るための正規表現は3つあるためにクエリが複雑になり、パフォーマンスが低下します。正規表現のマルチキャプチャとIf関数を使用してクエリを簡略化しましょう。

正規表現のマルチキャプチャ

正規表現のマルチキャプチャを使用すると、NRQL変数の多重代入機能を使用して、一つの属性から単一の正規表現を使って複数の値(最大16個)を取得できます。多重代入は、正規表現のキャプチャで指定された名前を使用してキャプチャされた値を取得するため、代入の順序は関係ありません。

...WITH capture(message, r'(?P<A>.)(?P<C>.)(?P<B>.)') AS (A, B, C)...

If関数

If関数は、New Relicクエリに条件を追加する別の方法を提供します。Ifは以下の構造を持っています:

If(<condition>, <trueValue>, [falseValue])

  • Ifの最初の引数は、WHERE句やpercentageおよびfilterなどの関数と同様に、条件を指定します。
  • 2番目と3番目の引数は、条件の結果に基づいて返される値です。これらの引数は、固定値またはround(attribute)のような計算値のいずれかです。3番目の引数はオプションであり、デフォルトで nullを返します。

詳細については、NRQLドキュメントを参照してください。

正規表現のマルチキャプチャを使用してクエリを簡素化する

これらの機能を使用すると、クエリを次のように簡略化できます。

FROM Log 
WITH capture(message, r'.*method=(?P<method>[[:alpha:]]+)id=.*took=(?P<value>\d*\.?\d*) (?P<units>µs|ms)') AS (method, value, units), 
  numeric(value) / if(units = 'µs', 1000, 1) AS duration 
SELECT percentile(duration, 99) AS 'duration 99th' 
FACET method

これでクエリは1つの正規表現を使用して3つの値をすべてキャプチャし、さらにIf 関数を使用して、ユニットを扱う際の複雑な処理を簡単にできました。

このクエリを単一の正規表現に変換すると、クエリを実行するための計算コストが削減され、ロードバーを見つめる時間が短くなります。この例の場合、クエリは約280%高速化(7 秒→2.5秒)されます。

重要なメモ

  • If関数には、filterまたは percentage関数に見られるような、フィルター条件の前のWHERE句が含まれません。このリリースでは、WHEREはオプションです。クエリを正しく解釈する必要のあるエッジケースが他にもいくつかあることにご留意ください。

アンカーパース

直前のクエリをさらに洗練させることができる新機能がもう一つあります。それは正規表現です。正規表現のcaptureは、LIKEに似た構文を使用して値をキャプチャできる、より簡単な(そしてより高速な)新しい関数AnchorParse(またはaparse)に置き換えることができます。この新しい関数は以下の形式となっています。

AnchorParse(<evaluable>, <capturePattern>)

aparse(<evaluable>, <capturePattern>)

新しい関数の引数はcaptureと同様のフォーマットですが、パターンは以下のようなワイルドカードを使用することでLIKE句と同様に動作します:

  • %は、LIKE句に見られるような、キャプチャされないワイルドカードです。
  • *は、正規表現のキャプチャと同様にキャプチャされるワイルドカードです。

aparseに関するより詳細な情報はNRQLドキュメントを参照してください。

今回の例では、これらの値を抽出するために高度な正規表現マッチング動作を必要としないため、aparseを使用してクエリをさらに簡略化できます。

FROM Log 
WITH aparse(message,'%method=* id=%took=* *') AS (method, value, units), 
  numeric(value) / if(units = 'µs', 1000, 1) AS duration 
SELECT percentile(duration, 99) AS 'duration 99th' 
FACET method

複雑な正規表現のキャプチャグループの代わりに、解析するテキストの場所(*) をアンカー("method=", "took=")とワイルドカード(%)で宣言して、一致方法と場所のパターンを作成するだけです。

このクエリを正規表現からアンカー解析に変換すると、クエリを実行するための計算コストがさらに削減され、ロードバーを見つめる時間がさらに短くなります。この場合、クエリはさらに150%高速化(2.5秒→1.6秒)されます。

重要なメモ

  • 上記のアンカー解析では、値とユニットの間のスペースを利用します。そのスペースがなければ、正規表現のキャプチャが依然として必要です。
  • aparseは、(名前の付いたキャプチャグループを使用して値を抽出する)正規表現のマルチキャプチャと異なり、宣言の順序を使用するため、変数の割り当て順序重要です。正規表現の
  • 正規表現のcaptureと同様、aparseのすべての結果は文字列のままです。したがって、例が示しているように、必要に応じてnumeric関数を使用して文字列を数字に変換しなければなりません。

結論

上記の機能を使用すると、いくつかの複雑なクエリ、特に構造化されたテキストデータを操作するクエリを簡素化できます。クエリの読み書きの仕方を変えるためには時間がかかりますが、そうすることではるかに高速で読みやすいクエリを作成できるようになります。例に挙げたクエリは、当初のものよりもはるかに簡潔で読みやすくなり、ほぼ440%高速化(7秒→1.6秒)されました。

この例が、新機能を使用して複雑なテキストデータをより適切に解析し理解できるための参考となることを願っています。この例ではログメッセージに焦点を当てていますが、今回紹介した新機能は一般にはすべてのイベントタイプとデータタイプに適用できます。