Cloud Functions for Firebaseを楽々デバッグするには

Firebase Advent Calendarトリなのに地味な内容で恐縮なんですが、Cloud Functions for Firebase(以下Functionsと呼びます)のデバッグ方法についてまとめてみました。

ログについて
------------------------------
まず最初にFunctionsでのログの見方・出し方をご紹介します。

### 見方
以下の３通りの方法があります。

* Webコンソールで見る
* functions:logs
* Stackdriverで見る

丸投げですがこちらのトピックに関しては既にAdvent Calendarに記事があったので貼っておきます。
[Cloud Functions for Firebase のログをコマンドラインで確認する](https://qiita.com/star__hoshi/items/f2c59d9b6ae0b3f4977c)
### 出し方
そもそもFunctionsのログは関数が実行された時にデフォルトで

* いつ実行したか
* いつ実行が終了したか。終了するまでに何msかかったか
* ランタイムエラーが起きた時のスタックトレース

をログに書いてくれます。
こんな感じです。

<img width="534" alt="example log.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/150569/f48c9bdb-5c74-dc6b-1994-3761058e6b4c.png">


それに加えて任意のタイミングでログを出したい場合には `console.xxx`を使うことができます。Functionsのログのログレベルは `error`, `info`, `debug`　の３種類があり、それを出し分けることができます。

試しに `console.xxx` でどんなログが出るか試してみました。結果としてはこんな感じ↓

<img width="1057" alt="スクリーンショット 2017-12-07 22.02.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/150569/4fbf22de-9b3d-cbfb-bc7e-75c74108b97f.png">


||ログレベル|
|:---|:---|
| console.log() | info |
| console.warn() | error |
| console.info() | info |
| console.error() | error |

あんまり使い分けるメリットもなさそうなので `console.log` と `console.error` だけ覚えておけば良いと思います。

ちなみに `console.debug()` はクラッシュしてしまいました。ログレベル `デバッグ`　はデフォルトのログ以外にどうやれば出すことができるのか分からないですが、まあそんな感じです。

###ちなみに
ログに色はつきませんでした。残念。

```js
console.log("%c黄色になるかな？", "color: yellow;");
```

<img width="752" alt="スクリーンショット 2017-12-07 22.04.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/150569/176faf75-7c4c-e523-ae5c-55971227ef37.png">


###参考
* [Writing and Viewing Logs](https://cloud.google.com/functions/docs/monitoring/logging)


デバッグ方法1: ローカルでHTTPベースの関数を実行する
------------------------------
それではデバッグ方法について見ていきます。まずは定番？の `firebase serve`。 
`firebase serve` コマンドを使うことによってローカルで関数がHTTP経由で叩けるようにします。

例えばこんな関数があるとして

```js
exports.hello = functions.https.onRequest((request, response) => {
  response.status(200).send('whatsup!');
});
```

  `firebase serve --only functions`

というコマンドを実行すると

  `http://localhost:5000/your-firebase-proj/region/hello`

という風にUrlが表示されるはずなので、あとはそのUrlにリクエストを送るだけで関数が実行されます。ログなどもローカルのCLIに表示されるので、それを駆使してデバッグに役立てることができるでしょう。

ただ、この方法はHTTPベースで実行できる関数に対しては有効ですが、データベースのアップデートなどイベントに対するトリガー関数に関してはテストできません。

このためお手軽ではありますが、用途が限られた手法ではあります。

デバッグ方法2: Cloud Functions シェルを利用する
------------------------------
対話形式でローカルで関数を実行できるシェルです。HTTP関数以外のRealtime Database、pub/sub、Firestoreなんでもエミュレートできるので非常に便利です。ログもちゃんとCLI上に吐き出してくれます。

早速使い方なんですが、Realtime Databaseに入ってきたテキストを自動的に大文字に変換する関数を例にご説明します。

```js:index.js
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original').onCreate(event => {
  const original = event.data.val()
  console.log('Uppercasing', event.params.pushId, original)
  const uppercase = original.toUpperCase()
  return event.data.ref.parent.child('uppercase').set(uppercase)
})
```

それではエミュレーターを立ち上げてみましょう！以下のコマンドをCLIで実行します。

    firebase experimental:functions:shell

すると以下のように対話形式のシェルが立ち上がるはずです。
<img width="424" alt="init firebase cli.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/150569/57cf4139-7c61-224c-d734-3fe4504f7a5a.png">


そして `Uppercase("foo")` を入力して実行してみます。コンソールを見たらしっかり大文字になったものが反映されていました。
<img width="214" alt="result.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/150569/07f42dd8-fdb9-33df-1196-5504f61b38ce.png">


また、パラメータである `pushId` も自動で作ってくれていますね。このパラメータは自分でつけるようにすることもできるみたいです。

    makeUppercase('foo', {params: {pushId: 'custom_push_id'}})

ちなみにこのシェルは一々再起動しなくても、関数を編集した内容を保存したら反映されます。準備もほとんどかからず大変便利ですね。

他にも個人的によく使いそうだなと思ったものを以下にまとめます。

### HTTP関数
####メソッドを指定
```js
myHttpsFunction() //何も指定しない場合はGETになります
myHttpsFunction.get()
myHttpsFunction.post()
```

#### パスやクリパラメータの指定
クエリパラメータをつけたりもできます。

```js
myHttpsFunction("/path")
myHttpsFunction("?test=hogehoge")
```

### Realtime Databaseトリガー
#### onUpdate や onWrite時に実行
```js
myDatabaseFunction({before: 'old_data', after: 'new_data' })
```

#### 認証を書き換え
```js
myDatabaseFunction('data', {auth: {admin: false}} //認証されていないユーザとして実行
myDatabaseFunction('data', {auth: {variable: {uid: 'abcd'}}} //特定のユーザのIDを指定
```

その他にも色んなユースケースに対応しています。詳しいメソッドや引数の説明についてはこちらのドキュメントを読んでみてください。
[ローカルでの関数の実行](https://firebase.google.com/docs/functions/local-emulator)

###参考
https://developers-jp.googleblog.com/2017/10/testing-functions-locally-with-cloud.html

感想
------------------------------
Functionsシェルがかなりいい感じですね。使い慣れれば強力なお供になってくれそうです。一々デプロイして試すのも中々にめんどくさいのでよろしければ一度お試しください！