Twilio CLI（サーバーレス応用編②）

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズのTwilio事業部エバンジェリストの高橋です。

今回は、Twilio CLIを使ったサーバーレス環境の構築の応用編②と題して、Functionのデバッグ方法について説明します。

Twilio CLIについては、以下の記事も御覧ください。

- [Twilio CLI（セットアップ編）](https://qiita.com/mobilebiz/items/456ce8b455f6aa84cc1e)
- [Twilio CLI（API操作編）](https://qiita.com/mobilebiz/private/0c687a1cd66772885d6e)
- [Twilio CLI（サーバーレス開発編）](https://qiita.com/mobilebiz/items/fb4439bf162098e345ae)
- [Twilio CLI（サーバーレス応用編①）](https://qiita.com/mobilebiz/items/27faffcc93725e512aea)

# Functionのログを確認する

Functionの内部で`console.log()`などを利用してログを書き出すことがあるかと思います。ここでは、出力されたログをどのようにして確認するかを解説します。

まずは、Twilio CLIを使って、hello worldプロジェクトを作成してください。

```sh
twilio serverless:init hello-world
```

## サンプルプログラム
できあがったプロジェクトのfunctionsフォルダ内にあるhello-world.jsを以下のように少し改造してみます。
2行目に`console.log('hello-world.js running.');`を追加しました。

```javascript
exports.handler = function(context, event, callback) {
  console.log('hello-world.js running.');
  const twiml = new Twilio.twiml.VoiceResponse();
  twiml.say('Hello World!');
  callback(null, twiml);
};
```

## ローカル実行時

このFunctionをローカルで実行するには、以下のコマンドを実行します。

```sh
twilio serverless:start
```

しばらくすると、以下のようなメッセージが表示されて、ローカル実行が可能になります。

```sh
┌────────────────────────────────────────────────────────────────────┐
│                                                                    │
│   Twilio functions available:                                      │
│   ├── /private-message | http://localhost:3000/private-message     │
│   ├── /hello-world | http://localhost:3000/hello-world             │
│   └── [protected] /sms/reply | http://localhost:3000/sms/reply     │
│                                                                    │
│   Twilio assets available:                                         │
│   ├── /index.html | http://localhost:3000/index.html               │
│   ├── /style.css | http://localhost:3000/style.css                 │
│   └── [private] /message.js | Runtime.getAssets()['/message.js']   │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘
```

このメッセージの中の、`http://localhost:3000/hello-world`の部分を、`cmd`キーを押しながらクリックすると、ブラウザ上に以下のTwiMLが表示されます。

```xml
<Response>
<Say>Hello World!</Say>
</Response>
```

console.logの結果については、メッセージの下のところに、以下のように表示されているはずです。

```sh
hello-world.js running.
200 GET /hello-world │ Response Type text/xml; charset=utf-8
```

このように、ローカル実行時はコンソール画面にログが表示されます。

## リモート（Twilio上）実行時

では、このプログラムをサーバーにデプロイしてみましょう。
デプロイは以下のコマンドです。

```sh
twilio serverless:deploy
```

しばらくして、以下のようなメッセージとともにデプロイが完了します。

```sh
✔ Serverless project successfully deployed

Deployment Details

（中略）

https://www.twilio.com/console/assets/api/ZSc91cf9c078c7e45f6e00918c6bed009e/environment/ZEe13e98fd4bfd8cef6c83bc49c2928954
Functions:
   [protected] https://hello-world-XXXX-dev.twil.io/sms/reply
   https://hello-world-XXXX-dev.twil.io/hello-world
   https://hello-world-XXXX-dev.twil.io/private-message
Assets:
   [private] Runtime.getAssets()['/message.js']
   https://hello-world-XXXX-dev.twil.io/index.html
   https://hello-world-XXXX-dev.twil.io/style.css
```

ここでも同じように、`https://hello-world-XXXX-dev.twil.io/hello-world`の部分を`cmd`キーを押しながらクリックすると、先ほどと同じようにブラウザ上にTwiMLが表示されます。
**ただし、今回はコンソールにはログは表示されません。**

Twilio上で実行されたFunctionのログについては、管理コンソール経由で確認できます。

- Twilio管理コンソールにログインします。
- スライドメニューから**Functions**を選択します。
- さらに**api**を選択します。

![Functions_api.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/f1c275b5-620a-4dda-5d34-05f52d8f49aa.png)
- hello-worldプロジェクトの**Environments**内にある**Show Logs**をクリックします。

![Functions_api_environment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/e2114b49-3a28-595f-80a2-23aeeb0e252b.png)

以下のような画面が表示されるかと思います。

![スクリーンショット 2020-04-14 15.56.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/55d0dced-2e48-22c0-454f-0bc799b8234e.png)

この画面は、表示してから実行された分のログしか表示されないため、先程実行したログは表示されていません。

- `/hello-world`のコピーアイコンをクリックし、別のブラウザタブ上で開いてみます。
- TwiMLが表示されるのを確認し、前のタブに戻ってみます。
- 以下のようにログが表示されているはずです。

![スクリーンショット 2020-04-14 17.50.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/31dbc809-3f3e-ad92-c6db-35b2f25d322a.png)

**このログ表示画面は、時間が経つと同期しなくなってしまうので、もしログが表示されなくなった場合は、一度Functionsのapiメニューに戻って、もう一度Show Logsを選択してください。**

# Functionをデバッグする

Functionをコーディングをしているときに、思った動作をしないのでデバッグしたくなることあります。
ここでは、サーバーにデプロイする前に、ローカルでデバッグする方法を解説します。

## VS Codeを使ったデバッグ方法

今回は、VS Codeのデバッグ機能を使った方法をご紹介します。

- 先程実行した`hello-world.js`をVS Codeで開きます。

![スクリーンショット 2020-04-14 18.01.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/9dce6517-9ec9-ec83-8b4f-7752baecfec7.png)

- **実行とデバッグ**（三角マークに虫のアイコン）に移動します。
![スクリーンショット 2020-04-14 18.02.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/566ccbe3-b2a1-209e-1735-e8eb0a58b841.png)

- **launch.jsonファイルを作成します**をクリックします。
- 環境の選択プルダウンが表示されたら、**Node.js**を選択します。

![スクリーンショット 2020-04-14 18.04.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/43e9e117-e5e0-f76c-1c22-6dd9490a856e.png)

- `launch.json`ファイルを以下のように編集します。

```json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "attach",
            "name": "Launch Program",
            "address": "localhost",
            "port": 9229
        }
    ]
}
```

- VS Codeのターミナル画面で、以下のコマンドを入力し、デバッグモードでローカル実行します。

```sh
twilio serverless:start --inspect=""
```

- 次のようなメッセージが表示されて、デバッグモードで実行されます。

```sh
Debugger listening on ws://127.0.0.1:9229/b9053561-1172-4645-bf0c-cd266eff55b3
For help, see: https://nodejs.org/en/docs/inspector
┌────────────────────────────────────────────────────────────────────┐
│                                                                    │
│   Twilio functions available:                                      │
│   ├── /hello-world | http://localhost:3000/hello-world             │
│   ├── /private-message | http://localhost:3000/private-message     │
│   └── [protected] /sms/reply | http://localhost:3000/sms/reply     │
│                                                                    │
│   Twilio assets available:                                         │
│   ├── /index.html | http://localhost:3000/index.html               │
│   ├── [private] /message.js | Runtime.getAssets()['/message.js']   │
│   └── /style.css | http://localhost:3000/style.css                 │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘
```

- `hello-world.js`の2行目にブレークポイントを設定します（行数の左側をクリック）。
- さらにRunウィンドウの上部にある緑の矢印を押します。

![Function_debug.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/a8ba6332-a4df-0ef2-5ec6-979b460c15e9.png)

- デバッグが始まります。
- ブラウザ上で、`http://localhost/hello-world`を開きます。
- ブレークポイントでプログラムが一時停止します。

![スクリーンショット 2020-04-14 18.20.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/7e848632-1eb7-13f8-b471-57b5a6a4268a.png)

- あとは画面上部の**ステップオーバー**などを操作したり、Runウィンドウ上で変数を確認するなどデバッグが可能です。

## Twilioと連携したデバッグ

実施にTwilioと連携してデバッグしたい場合は、``--ngrok``オプションを付けてFunctionをローカルで実行し、TwilioからのWebhookを受けられるようにしておきます。

```sh
twilio serverless:start --ngrok="" --inspect=""
```

この状態でも先程と同じようにデバッグが可能になるので、Twilioから渡ってくるパラメータを確認したり、ステップ実行させたりすることができます。
![スクリーンショット 2020-04-15 08.34.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/86599992-bdce-b443-f07d-6a901a740591.png)

ただし、TwilioからのWebhookは**10秒のタイムアウト制限**がありますので、10秒以内に応答を返さないと、サーバー上で`Error 11205 HTTP connection failure`が発生します。

## デバッグの終了

- デバッグを終了するには、**切断**アイコン（右側のオレンジ色のアイコン）をクリックします。
- VS Codeのターミナル画面も`Ctrl+C`でデバッグ実行を終了しておきます。

# まとめ

今回は、Twilio Functionsを使ってデバッグする方法をお伝えしました。本記事では、localhostでの接続について説明しましたが、`--ngrok`オプションを使って、外部からのアクセスを有効にすることで、Twilioと連携したデバッグも可能です。
ぜひ活用してみてください。

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilioは音声通話、メッセージング（SMS/チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。