JSONからフィールドを抽出する。

[Splunk>Answers](https://answers.splunk.com/answers/802709/make-extractions-in-propsconf-from-search-query.html)の質問はなんか放置されてたけど、自分で納得できたので投稿します。

大体[.confの資料](https://conf.splunk.com/files/2017/slides/observations-and-recommendations-on-splunk-performance.pdf)に何故だかは書いてあった。
#課題
```java:invalidJson.spl
 | makeresults 
 | eval _raw="Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"global\", \"origin\": \"dynstats\", \"values\": { } }
  Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"imuxsock\", \"origin\": \"imuxsock\", \"submitted\": 0, \"ratelimit.discarded\": 0, \"ratelimit.numratelimiters\": 0 }
  Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"action 0\", \"origin\": \"core.action\", \"processed\": 50996, \"failed\": 0, \"suspended\": 0, \"suspended.duration\": 0, \"resumed\": 0 }
  Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"action 1\", \"origin\": \"core.action\", \"processed\": 50996, \"failed\": 0, \"suspended\": 0, \"suspended.duration\": 0, \"resumed\": 0 }" 
 | makemv delim="
  " _raw 
 | stats count by _raw 
 | rex "(?<json>{.*)" 
 | spath input=json
```
JSONが途中から出てくるログをどうやったら[props.conf](http://docs.splunk.com/Documentation/Splunk/latest/Admin/Propsconf)で抽出できるのかを質問してみた。
この段階では、抽出したフィールドから`INDEXED_EXTRACTIONS`とか使えると思っていた。
#解決方法
```perl:props.conf
 [json_sed]
 TIME_FORMAT = %B %d %T
 SEDCMD = s/.*?({.*)/\1/g
 KV_MODE = json
 LINE_BREAKER = ([\r\n]+)
 NO_BINARY_CHECK = true
```
一番危惧していたのは、 `SEDCMD`でログを加工してしまうとログの情報が取れなくなること。
でも順番で処理してくれるみたいで、最初に時刻情報の抽出をした後
`SEDCMD`でログを加工してしまえばいいらしい。
[transforms.conf](https://docs.splunk.com/Documentation/Splunk/latest/Admin/Transformsconf)ではなく`props.conf`でフィールド抽出は`EXTRACT`です。
これは`rex`の構文をそのまま使える。

今回は`LINE_BREAKER`をデフォルトにしている。
通常のJSONだと　`LINE_BREAKER = }(,)` あたりがだいたい使えるかも。ここはログ次第

```perl:sed_check.spl
| makeresults 
| eval _raw="Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"global\", \"origin\": \"dynstats\", \"values\": { } }
  Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"imuxsock\", \"origin\": \"imuxsock\", \"submitted\": 0, \"ratelimit.discarded\": 0, \"ratelimit.numratelimiters\": 0 }
  Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"action 0\", \"origin\": \"core.action\", \"processed\": 50996, \"failed\": 0, \"suspended\": 0, \"suspended.duration\": 0, \"resumed\": 0 }
  Nov 14 03:23:42 hostname rsyslogd-pstats:{ \"name\": \"action 1\", \"origin\": \"core.action\", \"processed\": 50996, \"failed\": 0, \"suspended\": 0, \"suspended.duration\": 0, \"resumed\": 0 }" 
| makemv delim="
  " _raw 
| stats count by _raw 
| rex mode=sed "s/^.*?({.*})/\1/g"
```
こういった形で確認して、そのまま`SEDCMD`の引数に持っていけばいい。
`|spath`が効くなら`KV_MODE=json`も使える。
なお、`LINE_BREAKER`は`rex "(?<line_breaker>なにか)"`して`split(_raw,"なにか")`が使えるところと考えるといいと思います 
:sweat:わかりずらいですね

#JSONのJSON

```java:json_json.spl
 | makeresults 
 | eval _raw="{
 @timestamp: 2020-02-05T09:41:19.486+00:00
 domain: capem
 environment: sit
 level: INFO
 logger_name: com.test.dna.evthub.sse.impl.EventEncrypter
 message: {\"data\":{\"errorDetails\":[{\"system\":\"OCS\",\"responseCode\":404,\"request\":{\"url\":\"https://slot4.org008.t-dev.test.net/application/ocsia/v1/ocs-provisioning/service/61474817171/products\",\"body\":[\"fb92a747-1cf7-09c8-33fc-0da0d0c16d80\"]},\"response\":{\"statusCode\":404,\"error\":{\"error\":10004,\"message\":\"Service not found in OCS\"}}}],\"transactionDetails\":{\"id\":\"30d49584-76fe-4e9e-b7b4-0c3a819e432e\",\"groupId\":\"6e2d25f3-6e77-90a2-689d-1e1476e79c8b\",\"parentId\":\"fb92a747-1cf7-09c8-33fc-0da0d0c16d80\",\"serviceId\":\"61474817171\",\"downstreams\":[{\"name\":\"OCS\",\"status\":\"FAILED\",\"statusCode\":404,\"error\":{\"error\":10004,\"message\":\"Service not found in OCS\"}}],\"orderItemId\":\"5065705155871632216\",\"actionStatus\":\"FAILED\",\"dependencies\":[],\"chargingSpecId\":\"CS_SVCLSUB_001\",\"chargingSpecType\":\"SERVICE_SUBSCRIPTION\",\"productActionCode\":\"CEASE\",\"productActionType\":\"cease\",\"productInstanceId\":\"fb92a747-1cf7-09c8-33fc-0da0d0c16d80\",\"chargingSpecSubType\":\"HANDSET_CONNECTION\",\"customerAccountUuid\":\"6b1b147c-2b98-2489-cf92-cefab92a77cf\",\"orderItemActionType\":\"Create\",\"effectiveDate\":\"2020-02-05T09:40:14+00:00\",\"sourceSystem\":\"B2C-Vlocity\",\"orderId\":\"B20052034417634\"}},\"correlationId\":\"30d49584-76fe-4e9e-b7b4-0c3a819e432e\",\"eventName\":\"WTC_SubscriptionLineItemCompletion_Failed\",\"timestamp\":\"2020-02-05T09:40:14+00:00\",\"eventPublisher\":\"WTC\"}
 thread_name: main
 }" 
 | rex "message: (?<message>{.*})" 
 | spath input=message
```
通常であれば、_message_だけ取り出すところ
#むりやりJSON
```perl:props.conf
SEDCMD-trim1 = (\S+)\:\s(\w\S+)/\"\1\":\s\"\2\",/g
SEDCMD-trim2 = message:\s({.*})/\"message\": \1,/
KV_MODE = json
```

```perl:rex
 | rex mode=sed "s/(\S+)\: (\w\S+)/\"\1\": \"\2\",/g"
 | rex mode=sed "s/message: ({.*})/\"message\": \1,/"
 | spath
```
が効くので、そのまま`SEDCMD`に移植。

#まとめ
JSONなログは`LINE_BREAKER`を気をつけないといけなかったり、まずは`|spath`が効くかどうか確認しないとフィールドが抽出できない。
ネストが深くなければREGEXで抽出もありだけど、自動抽出が便利すぎて、[無理やりJSON](https://ja.wikipedia.org/wiki/%E5%8E%9A%E5%88%87%E3%82%8A%E3%82%B8%E3%82%A7%E3%82%A4%E3%82%BD%E3%83%B3)のほうがいいです。