本記事は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
の値にはμs
とms
の両方があります。従来、これらのログを標準化された時間単位で抽出するのは困難でした。生産性を高めるための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秒)されました。
この例が、新機能を使用して複雑なテキストデータをより適切に解析し理解できるための参考となることを願っています。この例ではログメッセージに焦点を当てていますが、今回紹介した新機能は一般にはすべてのイベントタイプとデータタイプに適用できます。
次のステップ
- NRQLに関する当社のドキュメントを詳しくご覧ください。
- また、機能の改善点については、当社の教育サイトであるNerdbyteで確認してください。
- New Relicをまだお使いでない場合には、無料でNew Relicを使い始めることができます。無料アカウントには、毎月100GBの無料データ取込み、1名の無料フルアクセスユーザー、および無制限の無料ベーシックユーザーが含まれます。
本ブログに掲載されている見解は著者に所属するものであり、必ずしも New Relic 株式会社の公式見解であるわけではありません。また、本ブログには、外部サイトにアクセスするリンクが含まれる場合があります。それらリンク先の内容について、New Relic がいかなる保証も提供することはありません。