Splunk LINE_BREAKERについて

SplunkでJSONを扱うと配列(array`[]`)のところでイベントとして取り込みたい時があります。
その時は_props.conf_で`LINE_BREAKER`を指定する必要があります。

何かとSPLUNK>Answersでも質問があるし、以前正規表現で書いてあったことも少し足りていなかったので、まとめてみます。

#オフィシャル
[props.conf@Splunk>docs](https://docs.splunk.com/Documentation/Splunk/latest/Admin/Propsconf)

- `LINE_BREAKER = <regular expression>`
    - デフォルト値は`([\r\n]+)`なので`SHOULD_LINEMERGE=false`（←ここ重要）の時に各行で区切られる。

以下機械翻訳

- 行のマージが行われる前に、生のテキストストリームを初期イベントに分割する方法を決定する正規表現を指定します。 
- 正規表現には、キャプチャグループ（一致の識別されたサブコンポーネントを定義する括弧のペア）が含まれている必要があります。
    - [マイクロソフトのサイト](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/grouping-constructs-in-regular-expressions)が詳しい
- 正規表現が一致する場合は常に、Splunkソフトウェアは、最初のキャプチャグループの開始を前のイベントの終了と見なし、最初のキャプチャグループの終了を次のイベントの開始と見なします。
- 最初のキャプチャグループの内容は破棄され、どのイベントにも存在しません。このテキストは行の間にあることをSplunkソフトウェアに伝えています。

このあと、いろいろな使い方が載っている。

##解説

```console:log.txt
2021年01月08日 金曜日 10:07:54 AM



Employee3


Project Project Name Project Owner
------ ---------- -----------
A Y: \\Owner1\owner2
B Z: \\owner_1\owner 2
C g: \\owner11\owner12\owner 13

07 January 2021 18:21:56


Employee1


Project Project Name Project Owner
------ ---------- -----------
A Y: \\Owner1\owner2
B Z: \\owner_1\owner 2
C g: \\owner11\owner12\owner 13

Friday, January 8, 2021 10:04:44 AM


Employee2


Project Project Name Project Owner
------ ---------- -----------
A Y: \\Owner1\owner2
B Z: \\owner_1\owner 2
C g: \\owner11\owner12\owner 13

Thu Jan 7 22:33:44 EST 2021



Employee3


Project Project Name Project Owner
------ ---------- -----------
A Y: \\Owner1\owner2
B Z: \\owner_1\owner 2
C g: \\owner11\owner12\owner 13
```

[Multiple timestamps in a file@Splunk>Answers](https://community.splunk.com/t5/Developing-for-Splunk-Enterprise/Multiple-timestamps-in-a-file/m-p/536075#M4835)から

要件は

- ログには複数の項目が記載されていて、それを時間でイベントに分けたい
- ただし時間のフォーマットは複数あり

###props.conf
```configuration:props.conf
[many_datetime]
DATETIME_CONFIG = 
LINE_BREAKER = (?m)([\r\n]+).*\d\d\:\d\d\:\d\d($| [AP]M| \w{3} \d{4})
NO_BINARY_CHECK = true
SHOULD_LINEMERGE = false
category = Miscellaneous
pulldown_type = true
```
- `LINE_BREAKER`は最初のキャプチャーだけしか反応しないので、`($| [AP]M| \w{3} \d{4})`には反応しない。
- `(?m)`は正規表現のオプション_Multiline_。複数行のログを相手にする時にはつけると全体から探してくれるようになる。
    - 今回も行頭からログが始まっているからつけなくても大丈夫だと思っていたら日本語の時刻だと、`(?m)`がないとダメだった。
    - 複数行のログを一括で扱う時には`(?s)`_Singleline_。`.(ドット）`が`\n（改行）`を無視して全体で一致してくれるオプションと同時に使うと便利 :sweat_smile: その分正規表現が大変ですが

#JSON

```javascript:sample.json
{ [-]
   glossary: { [-]
     GlossDiv: { [-]
       GlossList: { [-]
         GlossEntry: { [-]
           Abbrev: ISO 8879:1986
           Acronym: SGML
           GlossDef: { [-]
             GlossSeeAlso: [ [-]
               GML
               XML
             ]
             para: A meta-markup language, used to create markup languages such as DocBook.
           }
           GlossSee: markup
           GlossTerm: Standard Generalized Markup Language
           ID: SGML
           SortAs: SGML
         }
       }
       title: S
     }
     title: example glossary
   }
}
```
[JSON Example](https://json.org/example.html)から

Splunkで取り込んだJSONをみるとこんな感じに**見える**。
でも実際は

```text:example.json
	
{
    "glossary": {
        "title": "example glossary",
		"GlossDiv": {
            "title": "S",
			"GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
					"SortAs": "SGML",
					"GlossTerm": "Standard Generalized Markup Language",
					"Acronym": "SGML",
					"Abbrev": "ISO 8879:1986",
					"GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
						"GlossSeeAlso": ["GML", "XML"]
                    },
					"GlossSee": "markup"
                }
            }
        }
    }
}
```
これは、イベントの`構文を強調表示`や`rawテキストとして表示`をクリックすることで切り替えることが可能

実際にログの取り込みについて検討する時は、**rawテキスト**でみていく必要がある。
上の例もAPIのレスポンスだと

```text:example.json
{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}}}
```
と１行

これを前提にイベントの区切りを考える必要がある。

```javascript:fruit.json
{"fruits":[{"fruit":"orange","location":"tray1","date":"6/20/2014","instances_1":[{"name":"orange","type":"citrus","shape":"round","status":"ok"}],"instances_2":[{"name":"orange","type":"citrus","shape":"round","status":"ok"},{"name":"orange","type":"citrus","shape":"round","status":"ok"},{"name":"orange","type":"citrus","shape":"round","status":"ok"}]},{"fruit":"lemon","location":"tray2","date":"6/20/2015","instances_1":[{"name":"a","type":"citrus","shape":"round","status":"ok"}],"instances_2":[{"name":"a","type":"citrus","shape":"round","status":"ok"},{"name":"b","type":"citrus","shape":"round","status":"ok"},{"name":"c","type":"citrus","shape":"round","status":"ok"}]},{"fruit":"clementine","location":"tray3","date":"6/20/2016","instances_1":[{"name":"a","type":"citrus","shape":"round","status":"ok"}],"instances_2":[{"name":"a","type":"citrus","shape":"round","status":"ok"},{"name":"b","type":"citrus","shape":"round","status":"ok"},{"name":"c","type":"citrus","shape":"round","status":"ok"}]}]}
```

サンプルとして[How to configure props.conf to parse JSON data structures?](https://community.splunk.com/t5/Getting-Data-In/How-to-configure-props-conf-to-parse-JSON-data-structures/m-p/129037)のJSONを取り込んでみる。
いい感じで日付も入っているのでわかりやすい。

##取り込み設定
```configuration:props.conf
[ example_json ]
CHARSET=UTF-8
KV_MODE=JSON
SHOULD_LINEMERGE=false
category=Structured
description=JavaScript Object Notation format.
disabled=false
pulldown_type=true
LINE_BREAKER=(.){\"fruit\"
SEDCMD-json=s/(?m)]}$// s/\"}$/"}]}/ s/{\"fruits\"://
TIME_FORMAT=%m/%d/%Y
TIME_PREFIX=date\":\s*\"
MAX_DAYS_AGO=4000
```

##解説

- `INDEXED_EXTRACTIONS`は何も加工しない時に使用できる。`SEDCMD`等でログそのものを加工する際は使えない。
    - `KV_MODE=JSON`を使用しましょう
- `LINE_BREKER`は`{\"fruit\"`の前の一文字で区切りを指定。今回は`,`だったり`[`
- 分割すると余計な部分が出てくるので、それを`SEDCMD`で削除かつ追加して正規なJSONフォーマットにしてあげている。
    - JSONフォーマットにしてあげるのは重要。きちんとしたフォーマットになればリスト表示で`構文を強調表示`の項目が出てくれる。
    - `SEDCMD`については[rex](https://docs.splunk.com/Documentation/Splunk/latest/SearchReference/Rex)の_sed mode_と一緒。なので置換側が正規表現じゃないことには注意

##サーチでの確認方法
データの取り込み画面でいろいろと試すのもありですが、正規表現の確認だとサーチ画面でやったほうがいろいろと試しやすい。

自分もSplunk>Answersで回答する場合、取り込まなくても設定が作れるので重宝しています。

詳しくは別途。

#その他いろいろ
- 区切る場所のキャプチャーする文字がない時は `LINE_BREAKER = ()何かの文字列`でもキチンと切ってくれる。
- キャプチャーする場所・区切る場所は`|`で区切って複数記述できる。なのであまり正規表現にこだわる必要はない。
    - `LINE_BREAKER = end(\n)begin|end2(\n)begin2|begin3`の例のとおり
    - `(?=なにか)`とか`(?<=なにか)`で頑張る必要はありません。:sweat:フィールド抽出は違いますが。
- 複数行のログでなんかキャプチャーできないなという時は`(?ms)`を使ってみよう。
    - [props.confにおけるLINE_BREAKERについて](https://qiita.com/toshikawa/items/062000fba552258d5289#propsconf%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8Bline_breaker%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)では自動って書いたけど、ログによるので。
    - 逆になんか反応多すぎって時は`(?-ms)`でオプションを明示的に外してみる。

#適用の順番
[Index time versus search time](https://docs.splunk.com/Documentation/Splunk/8.1.1/Indexer/Indextimeversussearchtime)によると


The following processes occur during index time:

1. Default field extraction (such as host, source, sourcetype, and timestamp)
- Static or dynamic host assignment for specific inputs
- Default host assignment overrides
- Source type customization
- Custom index-time field extraction
- Structured data field extraction
- Event timestamping
- **Event linebreaking**
- Event segmentation (also happens at search time)

の順番。`LINE_BREAKER`が動くのは８番目
加工したJSONが`INDEXED_EXTRACTIONS`で読み込めない理由がよくわかる。

#まとめ

`LINE_BREAKER`で適切にログを区切ってやると、読みやすくなるだけでなく、Splunkにも優しくなります。
今回は触れませんでしたが、_transforms.conf_で不要な部分とかは取り込まない設定ができますので、_props.conf_でやらなくても問題はないと思います。