Splunk: サーチマクロって何だ?


###### 実施環境： Splunk Free 8.2.2

### 0. 概要

 Splunk には、マクロ(サーチマクロ)という機能があります。
この機能は、 SPL 文でよく使用するパーツを簡単に使いまわせるようにするものです。
今回はそのマクロ機能について紹介していきます。

### 1. 設定方法

まず、「設定」から「詳細サーチ」を選択します。

![WS000066.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/b337576d-c7bc-932a-8d30-a7280478f42e.jpeg "設定")

「詳細サーチ」画面で、「サーチマクロ」を選択します。

![WS000067.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/66a5885d-fc54-2180-3cff-7c93275cf4f4.jpeg "詳細サーチ")

作成済みのサーチマクロの一覧が表示されます。
今回は新規作成となるので、右上の「新しいサーチマクロ」を押します。

![WS000068.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/b1df0bc0-47a1-48f3-cef8-6ac2718df72e.jpeg "サーチマクロ")

マクロの作成画面が表示されます。

![WS000069.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/3908e489-5fdb-c8d7-2131-43cc4d33364f.jpeg "新しいサーチマクロ")

各項目の説明は次の通りです。

+ 宛先 App (必須) : マクロを使用できる App 名
+ 名前(必須) : マクロの名前、引数を使用する場合は特殊な規則あり
+ 定義(必須) : マクロが対応する SPL パーツ
+  eval ベースの定義を使用しますか?(チェックボックス) : 定義が eval ベースの場合はチェックを入れる
+ 引数(任意) : マクロで使用する引数の名前
+ 検証式(任意) : 引数が適切かどうかを検証する eval ベースの判定式
+ 検証エラーメッセージ(任意) : 引数が不適切な場合に返すメッセージ

細かいルールについては後で述べます。
とりあえず今回は、次のように入力し、「保存」を押しましょう。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_count |
|定義| `stats count` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数|(設定値なし)|
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000070.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/f97e54e0-fba7-f66f-97a6-592b33c4bebd.jpeg "サーチマクロ作成")

これでマクロが作成できました。
一覧に表示されない場合は左上の App を変更して探してみてください。

![WS000071.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/5f48518e-f53e-89b2-28b4-9154b4c04835.jpeg "サーチマクロ一覧")

### 2. 使用方法

さて、では作成したマクロを実際に使用してみましょう。
次の SPL を実行してみてください。

```Splunk:Splunk
index="_internal"
| `test_count`
```

![WS000072.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/43ca8380-09ba-f361-c012-1f57cfdcaeb0.jpeg "サーチマクロ使用")

ログの件数がカウントできました。
これは、マクロの部分が`stats count`と置き換わったものと同じ動きです。

以下のように、マクロはバッククォート(\`)でマクロ名を囲うことで使用できます。

\`マクロ名\`

上記ではマクロは1つのコマンドを置き換えましたが、マクロが置き換えられる表現の範囲は結構広く、必ずしも1つのコマンドである必要はありません。
例えば以下のように、パイプを含む複数のコマンドを置き換えることも可能です。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_command |
|定義| `\| timechart span=1h count \| sort - count \| table _time` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数|(設定値なし)|
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000089.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/4b4a79a5-8ab9-cc59-d051-3d98e0bb2291.jpeg "複数コマンド1")

```Splunk:Splunk
index="_internal"
`test_command `
| eval _time = strftime(_time, "%Y/%m/%d %H:%M:%S")
| rename _time AS "日時"
```

![WS000090.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/0be1027c-3976-5b1a-0c68-ac5fa3fcb553.jpeg "複数コマンド2")

逆に、コマンドの一部のみを置き換えることも可能です。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_limit |
|定義| `limit=5` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数|(設定値なし)|
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000080.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/015a9bed-f1f3-490f-190f-19bdadf8c3b8.jpeg "コマンド一部1")

```Splunk:Splunk
index="_internal"
| sort `test_limit` _time
```

![WS000091.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/d0b7b65e-7bf8-123d-fbd5-2447cc4ec4c5.jpeg "コマンド一部2")

ただし、唯一気を付けるべき場合として、 search や inputlookup などの生成コマンド( generating commands )を定義に使用する場合は「定義の先頭にパイプ(|)を付けない」「マクロ使用箇所の直前にパイプを付ける」の2点を守る必要があります。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_lookup |
|定義| `inputlookup "geo_attr_countries.csv"` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数|(設定値なし)|
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000100.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/f31576db-e5b0-0086-872a-7946341c5524.jpeg "生成コマンド1")

```Splunk:Splunk
| `test_lookup`
```

![WS000101.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/100a9144-ef98-7b7d-5c69-8f7034ece2d4.jpeg "生成コマンド2")

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_lookup_pipe |
|定義| `\| inputlookup "geo_attr_countries.csv"` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数|(設定値なし)|
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000102.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/954d5b60-f51c-6a42-e549-62fd4ed3153f.jpeg "生成コマンド3")

```Splunk:Splunk
`test_lookup_pipe`
```

![WS000103.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/dcf4d86e-1ed0-37c8-9a2a-495368d13e66.jpeg "生成コマンド4")

### 3. 引数

サーチマクロでは、引数を指定して使用することができます。
引数を使用する方法は以下の通りです。

1. 「名前」の末尾に以下の形で引数の個数を指定します。
マクロの名前(引数の個数)

1. 「引数」に引数の名前をカンマ(,)区切りで指定します。
引数で使用できる文字は半角の英数字とアンダースコア(_)、ダッシュ(-)のみです。

1. 「定義」中の引数で置き換えたい箇所を以下のように指定します。
\\$引数の名前\\$

試しに以下のマクロを作成し、実行してみましょう。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_percent(2) |
|定義| `eval per = $part$ / $all$ * 100` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数| part,all |
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000092.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/d24a33ef-f914-5546-9206-50fb9f511f10.jpeg "引数1")

```Splunk:Splunk
| makeresults count=1
| eval num1 = 10, num2 = 20
| `test_percent( num1, num2)`
```

![WS000093.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/20b97649-0256-dba4-1bb5-1def5cc01142.jpeg "引数2")

なお、引数を使用する際に気を付けるべき点として、「引数にフィールド名を指定した場合も、マクロの展開時点では単なる文字列として扱われる」点が挙げられます。

例えば、以下のようなマクロを作ります。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_merge(2) |
|定義| `eval merge = "$str1$" . "$str2$"` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数| str1,str2 |
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000096.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/46e098d2-6a21-ae92-d216-f36fb1057ad2.jpeg "引数3")

そして、次の SPL 文を実行します。

```Splunk:Splunk
| makeresults count=1
| eval str_a = "aaa", str_b = "bbb"
| `test_merge( str_a, str_b)`
```

単純に考えると、上記の結果 merge の値には「 aaabbb 」が入るように思えます。
そして、実際に動かしてみた結果が以下です。

![WS000097.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/5956703f-6d41-756c-3fd0-b6bff0c0a219.jpeg "引数4")

 merge の値には「 str_astr_b 」が入っています。
これは一見奇妙に思えます。
ですが、先ほど述べた通り「マクロの展開時点では引数は単なる文字列である」ことを考えると、以下のように引数の箇所がフィールド名ではなく「文字列」となるため、展開されないことがわかります。

![WS000173.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/02c70726-a637-de98-d994-c17cf974e018.jpeg "文字列")

なので実は、以下のように引数に与える値をダブルクォーテーションでくくっても同じ結果になります。

```Splunk:Splunk
| makeresults count=1
| eval num1 = 10, num2 = 20
| `test_percent( "num1", "num2")`
```

```Splunk:Splunk
| makeresults count=1
| eval str_a = "aaa", str_b = "bbb"
| `test_merge( "str_a", "str_b")`
```

先の test_percent も、一見フィールドの値がマクロに与えられているように見えますが、実際の動きとしては「マクロの展開」→「フィールドの展開」という順番で動作しています。

この「マクロ展開時にフィールドが展開されない」というのは、後述の検証式でも同様です。
意外と引っ掛かりがちなので、気を付けましょう。

### 4. 引数の検証

サーチマクロで引数を指定する場合、「検証式」「検証エラーメッセージ」を指定することで引数が想定した形式かどうかを検証することができます。
検証の結果引数の形式が不適切である場合、そのマクロを使用している SPL はエラーとなります。

検証式は、 eval コマンド内で使用できる形で作成します。
検証式の結果は基本的に、 Boolean 形式になるように指定します。
 true なら成功、 false なら失敗です。

検証式を実際に使用してみます。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_percent_bool(2) |
|定義| `eval per = $part$ / $all$ * 100` |
| eval ベースの定義を使用しますか?|チェックを外す|
|引数| part,all |
|検証式| `$all$ != 0` |
|検証エラーメッセージ| Test Macro - Bad Args! |

![WS000104.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/ca21714d-191b-9463-0fb8-3066d787d608.jpeg "bool1")

```Splunk:Splunk
| makeresults count=1
| `test_percent_bool( 10, 20)`
```

![WS000105.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/8b3f0738-ccc2-04b2-4bf0-cafbcb23014f.jpeg "bool2")

```Splunk:Splunk
| makeresults count=1
| `test_percent_bool( 10, 0)`
```

![WS000106.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/82b5f3e2-a86b-6c6a-4ace-32161c1536f7.jpeg "bool3")

### 5. eval ベース

「 eval ベースの定義を使用しますか?」にチェックを入れると、 eval 関数や演算子を用いた表現でマクロの結果を返せます。
 「eval ベース」とは言っても eval コマンドでしか使用できないわけではなく、どのようなコマンドでも使用可能です。
定義内の関数や演算子の処理はマクロの展開時に行われ、その結果がここまで見てきたマクロの「定義」と同等、すなわち SPL 文にあてはめられた後で再度 SPL のルールに沿って解釈されます。
この形式を用いた場合、マクロの返す結果は必ず「単一の文字列」となる必要があり、「数値」などの場合はエラーとなります。

と、説明してきましたが、この形式は結構ややこしいので、以下の例をもとに実際の挙動を1歩ずつ確認してみましょう。

|項目名|設定値|
|:-|:-|
|宛先 App | search |
|名前| test_count_by(1) |
|定義| `"stats count BY " . substr("$name$", 1, 9)` |
| eval ベースの定義を使用しますか?|チェックを**入れる**|
|引数| name |
|検証式|(設定値なし)|
|検証エラーメッセージ|(設定値なし)|

![WS000112.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/ab5abcb1-80b7-55eb-e2c3-fd04e22f7fcc.jpeg "evalベース1")

```Splunk:Splunk
index="_internal"
| `test_count_by("log_level_test")`
```

![WS000113.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/054e1245-6889-1393-49aa-604f8ac3bc21.jpeg "evalベース2")

まず、 eval ベースの定義を展開します。

`"stats count BY " . substr("$name$", 1, 9)`

引数に与えた値は文字列「 log_level_test 」なので、

`"stats count BY " . substr("log_level_test", 1, 9)`

評価関数 substr は「引数1の文字列について、引数2で指定した文字数の箇所から引数3で指定した文字数分を切り出す関数」なので、文字列「 log_level_test 」の1文字目から9文字分を切り出し、

`"stats count BY " . "log_level"`

演算子ドット(.)は「文字列を結合する演算子」なので、

`"stats count BY log_level"`

よって、文字列「 stats count BY log_level 」がマクロの返す結果です。

この文字列をそのまま SPL に入れると

```Splunk:Splunk
index="_internal"
| stats count BY log_level
```

となり、これが先の SPL で実行された処理となります。
正直かなりわかりにくい形式ですし、そもそもこれを使わなくとも大抵のマクロは作成できるので、見かける機会はあまり多くないと思います。

### 6. マクロのエスケープ

使う機会はほぼないと思いますが、マクロ部分を単なる文字列として扱う場合は、ダブルクォーテーション(")で囲むことでエスケープします。

```Splunk:Splunk
| makeresults count=1
| eval test = "`test_limit`"
```

![WS000086.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/cb0dd6ef-04b0-1bfe-3050-68b684a6e4c8.jpeg "エスケープ")