Cloudflare Logpush 設定時の新(?)オプション (R2 で試す)

## logpull_options → output_options

Logpush のオプションが [新しくなっていた](https://developers.cloudflare.com/logs/reference/log-output-options/) のに気づいてなかった。

> Jobs in Logpush now have a new key output_options which replaces logpull_options and allows more flexible formatting.
>
> Edge Logstream jobs do not support this yet.

ログで取得するフィールドの指定を`logpull_options` から `output_options` へ変えて何が変わるか試す。

今回は R2 に置いたログで試したので、ついでに R2 のログの見方もメモしておく。
(直接読みに行く [Logs Engine](https://developers.cloudflare.com/logs/r2-log-retrieval/) ではない)


## 既存の設定を取る

既存の Logpush job の設定では `logpull_options` に取得するフィールドが列記されている。

R2 に吐いている HTTP Request の Logpush の設定を見てみる。

```bash
curl -Ls "https://api.cloudflare.com/client/v4/zones/$ZONEID/logpush/jobs" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: $CTYPE" \
| jq '.result[]|select (.destination_conf|(startswith("r2")))'

{
  "id": 132277,
  "dataset": "http_requests",
  "logstream": true,
  "frequency": "high",
  "kind": "",
  "enabled": true,
  "name": null,
  "logpull_options": "fields=ClientIP,ClientRequestHost,...,ZoneName&timestamps=rfc3339",
  "destination_conf": "r2://...
  :
  :
```

## 何が変わるか

1️⃣
`"logpull_options"` の 
>"fields=a,...,z&sample=0.1&timestamps=rfc3339" （&で連結）

を

`"output_options"` では
>{ "field_names": [a,...,z],
> "sample_rate": 0.1,
> "timestamp_format": "rfc3339" }

のように変えている。

`field_names` を独立させたことで、 jq で整形した API の出力（フィールド一覧）をサクッと放り込めるようになったのは、多少楽かも。

```bash
curl -Ls "https://api.cloudflare.com/client/v4/zones/$ZONEID/logpush/datasets/$DATASET/fields" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: $CTYPE" \
| jq -r -c '.result|keys'

["BotDetectionIDs","BotScore","BotScoreSrc","BotTags","CacheCacheStatus","CacheReserveUsed","CacheResponseBytes","CacheResponseStatus","CacheTieredFill","ClientASN","ClientCountry","ClientDeviceType","ClientIP","ClientIPClass","ClientMTLSAuthCertFingerprint","ClientMTLSAuthStatus","ClientRegionCode","ClientRequestBytes","ClientRequestHost","ClientRequestMethod","ClientRequestPath","ClientRequestProtocol","ClientRequestReferer","ClientRequestScheme","ClientRequestSource","ClientRequestURI","ClientRequestUserAgent","ClientSSLCipher","ClientSSLProtocol","ClientSrcPort","ClientTCPRTTMs","ClientXRequestedWith","ContentScanObjResults","ContentScanObjTypes","Cookies","EdgeCFConnectingO2O","EdgeColoCode","EdgeColoID","EdgeEndTimestamp","EdgePathingOp","EdgePathingSrc","EdgePathingStatus","EdgeRateLimitAction","EdgeRateLimitID","EdgeRequestHost","EdgeResponseBodyBytes","EdgeResponseBytes","EdgeResponseCompressionRatio","EdgeResponseContentType","EdgeResponseStatus","EdgeServerIP","EdgeStartTimestamp","EdgeTimeToFirstByteMs","FirewallMatchesActions","FirewallMatchesRuleIDs","FirewallMatchesSources","JA3Hash","OriginDNSResponseTimeMs","OriginIP","OriginRequestHeaderSendDurationMs","OriginResponseBytes","OriginResponseDurationMs","OriginResponseHTTPExpires","OriginResponseHTTPLastModified","OriginResponseHeaderReceiveDurationMs","OriginResponseStatus","OriginResponseTime","OriginSSLProtocol","OriginTCPHandshakeDurationMs","OriginTLSHandshakeDurationMs","ParentRayID","RayID","RequestHeaders","ResponseHeaders","SecurityAction","SecurityActions","SecurityLevel","SecurityRuleDescription","SecurityRuleID","SecurityRuleIDs","SecuritySources","SmartRouteColoID","UpperTierColoID","WAFAction","WAFAttackScore","WAFFlags","WAFMatchedVar","WAFProfile","WAFRCEAttackScore","WAFRuleID","WAFRuleMessage","WAFSQLiAttackScore","WAFXSSAttackScore","WorkerCPUTime","WorkerStatus","WorkerSubrequest","WorkerSubrequestCount","WorkerWallTimeUs","ZoneName"]
```

2️⃣
`"output_options"` の中で、出力内容をデフォルトから [変える](https://developers.cloudflare.com/logs/reference/log-output-options/#output-types) ことができる。（例はこの後）

3️⃣
`"logpull_options"` から `"output_options"` へのマイグレーションの仕方は [ココ](https://developers.cloudflare.com/logs/reference/log-output-options/#how-to-migrate) 。


## 設定を上書き

R2 に吐いている既存ログ（HTTP Request）は `"logpull_options"` に全てのフィールドを指定し、取得している。
これを `"output_options"` を使って `ClientIP` と `ClientRequestUserAgent` だけ取るように変える。

```bash
FIELDNAME='["ClientIP","ClientRequestUserAgent"]'; curl -Ls -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONEID/logpush/jobs/$JOBID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: $CTYPE" \
-d '{
  "output_options": {
      "field_names": '$FIELDNAME'
  }
}' | jq '.result'

{
  "id": 132277,
  "dataset": "http_requests",
  "logstream": true,
  "frequency": "high",
  "kind": "",
  "enabled": true,
  "name": null,
  "logpull_options": "fields=ClientIP,ClientRequestHost,...,ZoneName&timestamps=rfc3339",
  "output_options": {
    "field_names": [
      "ClientIP",
      "ClientRequestUserAgent"
    ]
  },
  "destination_conf": "r2://...
  :
  :
```

レスポンスを見ると、`"logpull_options"` はそのまま残っているが、
`"output_options":` に `"ClientIP"` と `"ClientRequestUserAgent"` が入っている。



## 結果

出力されたログを見ると `"output_options"` のほうが優先されていた。

設定後はファイルサイズが小さくなっている。

```bash
rclone ls $R2LP/20230625/

     1709 20230625T031240Z_20230625T031310Z_8586bb00.log.gz
     1683 20230625T031311Z_20230625T031337Z_8ecd537f.log.gz
       59 20230625T031337Z_20230625T031407Z_115d55df.log.gz
       59 20230625T031408Z_20230625T031438Z_22f09826.log.gz
```

ログの中身も IP とユーザーエージェント だけになっている。

```bash
rclone cat $R2LP/20230625/20230625T031337Z_20230625T031407Z_115d55df.log.gz  --s3-decompress | jq '.'

{
  "ClientIP": "*.*.*.*",
  "ClientRequestUserAgent": "grpc-go/1.52.0"
}
{
  "ClientIP": "*.*.*.*",
  "ClientRequestUserAgent": "grpc-go/1.52.0"
}
```


## 変形

Logpush の output は `ndjson` か `csv` になる。

ndjson も csv もデフォルトのルールで生成されるが、`"output_options"` を使うことで、出力形式をユーザが変更することができる。

公式の [サンプル](https://developers.cloudflare.com/logs/reference/log-output-options/#examples) を参考に試してみる。

（変数 FIELDNAME は上に記載の `["ClientIP","ClientRequestUserAgent"]` と同じ）

json のネスト
```bash
curl
  "output_options": {
      "field_names": '$FIELDNAME',
      "output_type": "ndjson",
      "batch_prefix": "{\"events\":[",
      "batch_suffix": "\n]}\n",
      "record_prefix": "\n  {\"info\":{",
      "record_suffix": "}}",
      "record_delimiter": ","
  }

結果
{"events":[
  {"info":{"ClientIP":"*.*.*.*","ClientRequestUserAgent":"grpc-go/1.52.0"}},
  {"info":{"ClientIP":"*.*.*.*","ClientRequestUserAgent":"grpc-go/1.52.0"}}
]}
```

CSV で出力

```bash
curl
  "output_options": {
      "field_names": '$FIELDNAME',
      "output_type": "csv"
  }

結果
"*.*.*.*","grpc-go/1.52.0"
"*.*.*.*","grpc-go/1.52.0"
```

CSV にヘッダを付ける

```bash
curl
  "output_options": {
      "field_names": '$FIELDNAME',
      "output_type": "csv",
      "batch_prefix": "C_IP,C_UA"
  }

結果
C_IP,C_UA
"*.*.*.*","grpc-go/1.52.0"
"*.*.*.*","grpc-go/1.52.0"
```

デリミタを TAB にする

```bash
curl
  "output_options": {
      "field_names": '$FIELDNAME',
      "output_type": "csv",
      "batch_prefix": "C_IP\tC_UA\n", "field_delimiter": "\t"
  }

結果
C_IP	C_UA
"*.*.*.*"	"grpc-go/1.52.0"
"*.*.*.*"	"grpc-go/1.52.0"
```

テンプレートを使った変形

```bash
curl
  "output_options": {
      "field_names": '$FIELDNAME',
      "output_type": "csv",
      "record_template": "CL_IP:{{.ClientIP}},CL_UA:{{.ClientRequestUserAgent}}"
  }

結果
CL_IP:"*.*.*.*",CL_UA:"grpc-go/1.52.0"
CL_IP:"*.*.*.*",CL_UA:"grpc-go/1.52.0"
```


```bash
curl
  "output_options": {
      "field_names": '$FIELDNAME',
      "output_type": "ndjson",
      "record_template": "\"CL_IP\":{{.ClientIP}},\"CL_UA\":{{.ClientRequestUserAgent}}"
  }

結果
{"CL_IP":"*.*.*.*","CL_UA":"grpc-go/1.52.0"}
{"CL_IP":"*.*.*.*","CL_UA":"grpc-go/1.52.0"}
```



## 後処理

もともとは全フィールドを取っていたので、元に戻す。
全フィールドは `jq` で取ればいいだけになった。
ありがたい。


```bash
FIELDNAME=`curl -Ls "https://api.cloudflare.com/client/v4/zones/$ZONEID/logpush/datasets/$DATASET/fields" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: $CTYPE" \
| jq -r -c '.result|keys'` && curl -Ls -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONEID/logpush/jobs/$JOBID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: $CTYPE" \
-d '{
  "output_options": {
      "field_names": '$FIELDNAME'
  }
}' | jq '.result'

{
  "id": 132277,
  "dataset": "http_requests",
  "logstream": true,
  "frequency": "high",
  "kind": "",
  "enabled": true,
  "name": null,
  "logpull_options": "fields=ClientIP,ClientRequestHost,...,ZoneName&timestamps=rfc3339",
  "output_options": {
    "field_names": [
      "BotDetectionIDs",
      "BotScore",
      "BotScoreSrc",
      :
      :
```


```bash
rclone cat $R2LP/20230625/20230625T034445Z_20230625T034515Z_b0b6dfd6.log.gz  --s3-decompress | jq '.'

{
  "BotDetectionIDs": [],
  "BotScore": 2,
  "BotScoreSrc": "Machine Learning",
  "BotTags": [],
  "CacheCacheStatus": "dynamic",
  "CacheReserveUsed": false,
  "CacheResponseBytes": 2819,
  "CacheResponseStatus": 526,
  :
```

全フィールを採取してもプロキシのパフォーマンスに影響がないのが、いいところ。

## R2 メモ

rclone を使うのが便利だった。
[R2 用の設定](https://rclone.org/s3/#cloudflare-r2)（rclone 公式）。

|内容|コマンド|オプション|
|-|-|-|
|R2 の Logpush 保存ディレクトリ一覧を取る|rclone ls||
|R2 の Logpush ファイルを見る|rclone cat |--s3-decompress (gzip を解いてくれる)|

今日最新のファイルを取って、その中身を見る

```bash
R2DIR="r2://logdir"
TODAY=`date '+%Y%m%d'`
LASTFILE=`rclone ls $R2DIR/$TODAY/ | tail -1 | awk -F' ' '{print $2}'`

rclone cat $R2DIR/$TODAY/$LASTFILE --s3-decompress
```