# 統計ウェブフック

*バージョン 2024.2.0 で追加。*

## 概要

統計情報をウェブフックで送信する機能です。

## 注意

`sora.conf` の [stats_webhook_url](SORA_CONF.html#1f750f) を有効にしない場合、 `log/stats_webhook.jsonl` は生成されません。

## ログ

統計ウェブフックのログは `log/stats_webhook.jsonl` と `log/stats_webhook_error.jsonl` に出力されます。

統計ウェブフックのログではデフォルトで出力されません。
[stats_webhook_url](SORA_CONF.html#1f750f) を有効にしたとしても [stats_webhook_log](SORA_CONF.html#2fa540) を `true` に設定するまでは、
ログが出力されません。

### stats_webhook.jsonl

統計ウェブフックで送信した **すべて** の処理を書き込みます。

### stats_webhook_error.jsonl

統計ウェブフックの送信が失敗した処理を書き込みます。

## 統計ウェブフックの間隔

Sora は一定間隔でクライアントへ統計情報を要求します。

統計ウェブフックの送信間隔はクライアントが送ってくる RTC 統計情報の間隔に依存します。デフォルトは 60 秒です。

### WebSocket シグナリングを利用している場合

[websocket_stats_timer_interval](SORA_CONF.html#67a631) に指定した間隔になります。

WebSocket の場合は `"type": "ping"` に `"stats": true` を付けて送信することで、
クライアントへ統計情報を要求します。

クライアントは `"type": "pong"` で `"stats": ...` に統計情報を含めて送ります。
この `"stats"` が統計ウェブフックの `"stats"` 項目として送信されます。

```mermaid
sequenceDiagram
    participant C as クライアント
    participant S as Sora
    participant A as アプリケーションサーバー
    note over C,S: WebRTC 確立
    S-)C: "type": "ping"<br>WebSocket
    C-)S: "type": "pong"<br>WebSocket

    S-)C: "type": "ping"<br>"stats": true<br>WebSocket
    C-)S: "type": "pong"<br>"stats": ...<br>WebSocket
    S->>+A: Stats over HTTP/1.1
    A->>-S: 200 OK
    note over C,S: 60 秒経過
    S-)C: "type": "ping"<br>"stats": true<br>WebSocket
    C-)S: "type": "pong"<br>"stats": ...<br>WebSocket
    S->>+A: Stats over HTTP/1.1
    A->>-S: 200 OK
```

### DataChannel シグナリングを利用している場合

[data_channel_stats_timer_interval](SORA_CONF.html#217b39) に指定した間隔になります。

DataChannel シグナリングの場合は `stats` ラベルを利用して、
`"type": "req-stats"` を送ってクライアントへ統計情報を要求します。

クライアントは `stats` ラベルを利用して、
`"type": "stats"` で `"reports"` に統計情報を含めて送ります。
この `"reports"` が統計ウェブフックの `"stats"` 項目として送信されます。

```mermaid
sequenceDiagram
    participant C as クライアント
    participant S as Sora
    participant A as アプリケーションサーバー
    note over C,S: WebRTC 確立
    S-)C: "type": "stats-req"<br>DataChannel
    C-)S: "type": "stats"<br>DataChannel
    S->>+A: Stats over HTTP/1.1
    A->>-S: 200 OK
    note over C,S: 60 秒経過
    S-)C: "type": "stats-req"<br>DataChannel
    C-)S: "type": "stats"<br>DataChannel
    S->>+A: Stats over HTTP/1.1
    A->>-S: 200 OK
```

## 設定

### stats_webhook_url

統計情報を送信するウェブフック URL を指定します。 HTTP または HTTPS の URL が指定できます。

```ini
stats_webhook_url = http://192.0.2.10:5890/sora/webhook/stats
```

### stats_webhook_log

統計情報ウェブフックのログを出力するかどうかを指定します。デフォルトは `false` で出力しません。

- 統計情報ウェブフックの送信した **全ての処理** は `log/stats_webhook.jsonl` ログに出力されます
- 統計情報ウェブフックの送信が **失敗した処理** は `log/stats_webhook_error.jsonl` ログに出力されます

```ini
stats_webhook_log = true
```

### ignore_connection_rtc_webhook

統計情報ウェブフックの `type: connection.rtc` を送信するかどうかを指定します。デフォルトは `false` で送信します。

### rtc_stats_log

統計情報ウェブフックの `type: connection.rtc` の `stats` をリストではなく個別に `rtc_stats.jsonl` として出力するどうかを指定します。

ログは `log/rtc_stats.jsonl` に出力されます。

```ini
rtc_stats_log = true
```

### stats_webhook_worker_number

統計ウェブフックのワーカー数を指定できます。
基本的にはデフォルトの 5 で足りますが、同時接続数が多い場合は変更をお勧めします。

```ini
stats_webhook_worker_number = 5
```

## 統計情報

統計情報をウェブフック URL に対して送信します

### 共通

- type- 統計情報の種類が入ります
- id- ウェブフック ID が入ります
- node_name- Sora のノード名が入ります
  - クラスター機能を利用していない場合は `sora@127.0.0.1` です
- version- Sora のバージョンが入ります
- label- sora.conf に設定された label が入ります
- timestamp- Sora が stats を送る直前の時間 RFC3339 形式 (UTC) が入ります

### HTTP ヘッダー

> **注釈**
>
> JSON のパース時の判断などに利用してください。

統計ウェブフックの HTTP ヘッダー に `sora-stats-webhook-type` というヘッダー名で統計ウェブフックのタイプが入ってきます。
`type` が `connection.rtc` の場合は `sora-stats-webhook-type: connection.rtc` が入ってきます。


### type: connection.rtc

クライアントから送られてくる統計情報です。

クライアント統計情報は W3C の [Identifiers for WebRTC's Statistics API](https://www.w3.org/TR/webrtc-stats/) に準拠している必要があります。

- type- `connection.rtc`
- channel_id- チャネル ID が入ります
- role
- group_id
- session_id- セッション ID が入ります
- client_id
- bundle_id
- connection_id
- simulcast
- spotlight
- stats- クライアントから送られてくる統計情報が入ります

```javascript
{
  "channel_id": "sora",
  "group_id": "78K3N8E5M551XEKEWR8QN77PHW",
  "session_id": "78K3N8E5M551XEKEWR8QN77PHW",
  "client_id": "NCD5EF3ME900ZDJ3SE27Y6AB6R",
  "bundle_id": "NCD5EF3ME900ZDJ3SE27Y6AB6R",
  "connection_id": "NCD5EF3ME900ZDJ3SE27Y6AB6R",
  "id": "N6W0DBF6A957B01BKNJJR1HC04",
  "label": "WebRTC SFU Sora",
  "spotlight": false,
  "simulcast": true,
  "stats": [
    {
      "id": "RTCAudioSource_5",
      "kind": "audio",
      "timestamp": 1629449091306.655,
      "type": "media-source",
      "audioLevel": 0.02237006744590594,
      "totalAudioEnergy": 0.15581033797981153,
      "totalSamplesDuration": 5.089999999999936,
      "trackIdentifier": "922dd031-8a4f-4122-9687-ce094fa11ee2"
    }, ...
  ],
  "timestamp": "2021-08-20T08:44:51.308778Z",
  "type": "connection.rtc",
  "node_name": "sora@127.0.0.1",
  "version": "2024.1.0",
  "log_written": false
}
```

> **警告**
>
> `stats` 項目はかなり大きな JSON になるため取り扱いには注意してください。







## stats 項目に含まれる統計情報の送信頻度

変化が無かったり変化の少ない `stats` 項目に含まれる統計情報を送る頻度を減らしています。

デフォルト 600 秒間隔にしています。

### type: connection.rtc

送信頻度を抑えている `RTCStatsType` は以下の通りです。

- codec- Sora の場合、初期値から変更される項目が無いため
- local-candidate- Sora の場合、初期値から変更される項目が無いため
- remote-candidate- Sora の場合、初期値から変更される項目が無いため
- certificate- Sora の場合、初期値から変更される項目が無いため
- peer-connection- Sora の場合、初期値から変更される項目が無いため
- track- DEPRECATED 項目のため
- stream- DEPRECATED 項目のため

これらの統計情報は 600 秒間隔で `stats` 項目に含まれるようになります。

## RTC 統計情報ログ出力

`sora.conf` で `rtc_stats_log = true` に設定すると、 `log/rtc_stats.jsonl` に RTC 統計情報が出力されます。
解析をしやすくするため、このログは `type: connection.rtc` の `stats` をリストではなく個別に出力します。

### 項目

- rtc_timestamp- RTCStats の timestamp が浮動小数点で入ります
- rtc_type- RTCStats の type が文字列で入ります
- rtc_id- RTCStats の id が文字列で入ります
- rtc_data- RTCStats がそのまま入ります

### 例

```javascript
{
   "connection_id": "16ZN7HHK5H3PFDFRZCZQN3WVD4",
   "id": "Z47B1V0DBN7R1AV90HY98CF6Y8",
   "label": "WebRTC SFU Sora",
   "timestamp": "2024-03-24T07:14:37.423158Z",
   "type": "connection.rtc",
   "version": "2024.1.0",
   "node_name": "sora@192.0.2.1",
   "group_id": "FVR7SV953D08ZA0Z3SVCF9HFBR",
   "session_id": "FVR7SV953D08ZA0Z3SVCF9HFBR",
   "role": "sendrecv",
   "channel_id": "sora",
   "client_id": "16ZN7HHK5H3PFDFRZCZQN3WVD4",
   "bundle_id": "16ZN7HHK5H3PFDFRZCZQN3WVD4",
   "spotlight": false,
   "simulcast": false,
   "log_written": true,
   "rtc_data": {
      "active": true,
      "id": "OTdata1A2223648171",
      "type": "outbound-rtp",
      "kind": "audio",
      "ssrc": 2223648171,
      "mid": "audio_jDlbho",
      "bytesSent": 117003,
      "codecId": "COTdata1_109_maxplaybackrate=48000;minptime=10;sprop-stereo=1;stereo=1;usedtx=0;useinbandfec=1",
      "headerBytesSent": 1248040,
      "mediaSourceId": "SA21",
      "mediaType": "audio",
      "nackCount": 0,
      "packetsSent": 39001,
      "remoteId": "RIA2223648171",
      "retransmittedBytesSent": 0,
      "retransmittedPacketsSent": 0,
      "targetBitrate": 64000,
      "totalPacketSendDelay": 0.0139959999999999997605,
      "transportId": "Tdata1"
   },
   "rtc_id": "OTdata1A2223648171",
   "rtc_timestamp": 1711264477421.78710938,
   "rtc_type": "outbound-rtp"
}
{
   "connection_id": "16ZN7HHK5H3PFDFRZCZQN3WVD4",
   "id": "Z47B1V0DBN7R1AV90HY98CF6Y8",
   "label": "WebRTC SFU Sora",
   "timestamp": "2024-03-24T07:14:37.423158Z",
   "type": "connection.rtc",
   "version": "2024.1.0",
   "node_name": "sora@192.0.2.1",
   "group_id": "FVR7SV953D08ZA0Z3SVCF9HFBR",
   "session_id": "FVR7SV953D08ZA0Z3SVCF9HFBR",
   "role": "sendrecv",
   "channel_id": "sora",
   "client_id": "16ZN7HHK5H3PFDFRZCZQN3WVD4",
   "spotlight": false,
   "simulcast": false,
   "log_written": true,
   "rtc_data": {
      "id": "Tdata1",
      "type": "transport",
      "bytesReceived": 104251,
      "bytesSent": 28783357,
      "dtlsCipher": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
      "dtlsRole": "client",
      "dtlsState": "connected",
      "iceLocalUsernameFragment": "90QS",
      "iceRole": "controlled",
      "iceState": "connected",
      "localCertificateId": "CF1F:6E:2B:47:36:FB:64:72:F9:D7:63:C3:16:6D:02:BB:72:62:E9:C0:E1:40:B6:13:E0:90:B2:E5:6C:49:98:6A",
      "packetsReceived": 1305,
      "packetsSent": 74835,
      "remoteCertificateId": "CF0E:AE:2F:F6:0D:EC:07:E4:27:13:34:38:02:EE:BB:EC:07:BA:77:9D:1C:B5:19:93:E0:A2:87:D5:A6:AA:6B:9A",
      "selectedCandidatePairChanges": 5,
      "selectedCandidatePairId": "CPLE514/b5_sVhdyppm",
      "srtpCipher": "AEAD_AES_128_GCM",
      "tlsVersion": "FEFD"
   },
   "rtc_id": "Tdata1",
   "rtc_timestamp": 1711264477421.78710938,
   "rtc_type": "transport"
}
```
