【SC-200】KQLクエリの書き方 – parse編

皆さんこんにちは。国井です。
前回紹介したKQLクエリの書き方シリーズの第11弾として parse 演算子を紹介します。
parse 演算子は Microsoft Sentinel で取得したログがひとつのログにまとめて入っちゃったときにカンマ区切りで別々の列に入れたい(=パース処理したい)ときに役立ちます。
例えばこんな時です。

let Traces = datatable(EventText:string)
[
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=23, lockTime=02/17/2016 08:40:01, releaseTime=02/17/2016 08:40:01, previousLockTime=02/17/2016 08:39:01)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=15, lockTime=02/17/2016 08:40:00, releaseTime=02/17/2016 08:40:00, previousLockTime=02/17/2016 08:39:00)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=20, lockTime=02/17/2016 08:40:01, releaseTime=02/17/2016 08:40:01, previousLockTime=02/17/2016 08:39:01)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=22, lockTime=02/17/2016 08:41:01, releaseTime=02/17/2016 08:41:00, previousLockTime=02/17/2016 08:40:01)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=16, lockTime=02/17/2016 08:41:00, releaseTime=02/17/2016 08:41:00, previousLockTime=02/17/2016 08:40:00)"
];
Traces

ここではサンプルでログそのものを作ってみたのですが、これを出力するとひとつの列に全部の情報が入ってしまうのです。

image

EventText列に全部の情報が入っていますが、これをカンマで区切った内容ごとに別々の列に格納したい。このようなときに parse 関数を使います。

let Traces = datatable(EventText:string)
[
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=23, lockTime=02/17/2016 08:40:01, releaseTime=02/17/2016 08:40:01, previousLockTime=02/17/2016 08:39:01)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=15, lockTime=02/17/2016 08:40:00, releaseTime=02/17/2016 08:40:00, previousLockTime=02/17/2016 08:39:00)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=20, lockTime=02/17/2016 08:40:01, releaseTime=02/17/2016 08:40:01, previousLockTime=02/17/2016 08:39:01)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=22, lockTime=02/17/2016 08:41:01, releaseTime=02/17/2016 08:41:00, previousLockTime=02/17/2016 08:40:01)",
"Event: NotifySliceRelease (resourceName=PipelineScheduler, totalSlices=27, sliceNumber=16, lockTime=02/17/2016 08:41:00, releaseTime=02/17/2016 08:41:00, previousLockTime=02/17/2016 08:40:00)"
];
Traces
| parse EventText with * "resourceName=" resourceName ", totalSlices=" totalSlices:long * "sliceNumber=" sliceNumber:long * "lockTime=" lockTime ", releaseTime=" releaseTime:date "," * "previousLockTime=" previousLockTime:date ")" *
| project resourceName, totalSlices, sliceNumber, lockTime, releaseTime, previousLockTime

順番に解説しますので最後の2行に注目してください。
まず parse 関数は | parse 元の列名 with.. で書き始めます。
続けて “resourceName=” resourceName と書くことで元の列に入っていた内容から resourceName= の後に書かれた文字列を新しく作った resourceName 列に格納しなさいと命令しています。
同様に totalSlices= の後に書かれた文字列は totalSlices 列へ、sliceNumber= の後に書かれた文字列は sliceNumber 列へ、lockTime= の後に書かれた文字列は lockTime 列へ、releaseTime=  の後に書かれた文字列は releaseTime 列へ、previousLockTime= の後に書かれた文字列は previousLockTime 列へそれぞれ格納しなさいと命令しています。(なお、totalSlices:long のように列名が指定されている場合、long型で値を格納するという意味になります)

最後に project を使って必要な列だけを指定してあげるとこんな感じで表示が変わります。

image

parse 演算子の中で使う * はどういう意味?

私にとって正規表現はあまり得意な分野ではないのですが..
MSサイトのドキュメント(https://learn.microsoft.com/ja-jp/azure/data-explorer/kusto/query/parseoperator)によると*は正規表現の.*?を表すとあります。つまりもっとも左側にある文字列を取り出します。そのため、

| parse EventText with * "resourceName=" resourceName

と書くことで、もっとも左側にある文字列であるresouceName=の後に続く文字列を引っ張ってこれるのです。また、このクエリでは

", totalSlices=" totalSlices:long

と記述している部分と

* "sliceNumber=" sliceNumber:long

と記述している部分があります。
“, totalSlices=”の場合は厳密に
, totalSlices=
に続く文字列を指定しているのに対して、
* “sliceNumber=”の場合はその前の文字列がなにであろうと
sliceNumber=
に続く文字列を指定しているという違いがあります。
(と私は解釈しましたが誤認識などあればご指摘いただけるとありがたいです)