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


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

ログについて

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

見方

以下の3通りの方法があります。

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

丸投げですがこちらのトピックに関しては既にAdvent Calendarに記事があったので貼っておきます。
Cloud Functions for Firebase のログをコマンドラインで確認する

出し方

そもそもFunctionsのログは関数が実行された時にデフォルトで

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

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

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

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

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

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

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

ちなみに

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

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

参考

デバッグ方法1: ローカルでHTTPベースの関数を実行する

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

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

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に入ってきたテキストを自動的に大文字に変換する関数を例にご説明します。

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

すると以下のように対話形式のシェルが立ち上がるはずです。

そして Uppercase("foo") を入力して実行してみます。コンソールを見たらしっかり大文字になったものが反映されていました。

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

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

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

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

HTTP関数

メソッドを指定

myHttpsFunction() //何も指定しない場合はGETになります
myHttpsFunction.get()
myHttpsFunction.post()

パスやクリパラメータの指定

クエリパラメータをつけたりもできます。

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

Realtime Databaseトリガー

onUpdate や onWrite時に実行

myDatabaseFunction({before: 'old_data', after: 'new_data' })

認証を書き換え

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

その他にも色んなユースケースに対応しています。詳しいメソッドや引数の説明についてはこちらのドキュメントを読んでみてください。
ローカルでの関数の実行

参考

感想

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