🍣

Firebase Clound Functionsのログ監視でSentryを使う時の試行錯誤

2020/12/25に公開

はじめに

Vue.jsのエラーログをsentryに送信することは、sentryでサインアップ後、示された手順に沿って進めていけば、簡単に設定ができました。

Firebase Cloud Functions(以下、Functionsと略します)のエラーログもsentryに送信したいと思い、試行錯誤したことをまとめておきたいと思います。

sentry以外の選択肢

まず、sentry以外のログ監視ツールを紹介します。

1.Fuctionsの管理コンソール

管理コンソールからも、ログを確認できます。シンプルなツールで、エラーを通知する機能などはなさそうです。

2.GCPのCloud Logging Error Reporting

下記のページに記載されていますが、GCPのログ監視ツールがあります。
https://firebase.google.com/docs/functions/reporting-errors
もしかしたら、こちらを使うのが、一般的な選択かもしれません。

今回は、フロントエンドのVue.jsと同じ監視ツールを使いたいという理由でFunctionsでもsentryを使うことにしました。sentryに不都合があれば、Cloud Logging Error Reportingに移行するかもしれません。

sentryのPlatformを選択

使えそうなPlatformは、下記の2種類あります。

  1. Node
  2. GCP Cloud Functions (Node)

Firebase Cloud Functionsは、GCP Cloud Functionsを元に作られているとはいえ、別物です。2の「GCP Cloud Functions (Node)」を使えるかどうかは、ドキュメントをざっと確認したり、ググっても判断できるドキュメントは見つかりませんでした。

まあ、とりあえず使ってみようということで、2のGCP Cloud Functions (Node)を選択しました。

GCP Cloud Functions (Node)を選択した理由

自分でラップ関数を書かなくていいからというのが理由です。

NodeのPlatformを使うと、汎用的に使うには、下記の記事のように、自分でラップ関数を書かないといけません。

https://medium.com/@goleary/using-sentry-io-with-firebase-functions-2d4456af162b

「GCP Cloud Functions (Node)」であれば、ラップ関数もsentryのSDKに含まれているので、便利です。

const Sentry = require("@sentry/serverless");

// dsnは自分の環境の設定を入力してください
Sentry.GCPFunction.init({
  dsn: "https://{xxxxx}@{yyyyy}.ingest.sentry.io/{zzzzz}",
  tracesSampleRate: 1.0,
});

exports.helloEvents = Sentry.GCPFunction.wrapEventFunction((data, context, callback) => {
  throw new Error('oh, hello there!');
});

Sentryの公式ドキュメント
https://docs.sentry.io/platforms/node/guides/gcp-functions/

導入には、sentryの管理画面で新規プロジェクトを追加し、Platformで「GCP Cloud Functions (Node)」を選択します。その後、「@sentry/serverless」というnpmパッケージがあるので、それをnpm installし、上記のようなコードを追加します。

試した「@sentry/serverless」のバージョンは、5.29.2です。

発生した問題

HTTPS callable functionsで、同期的に処理できない。エラーが発生しても、エラーを返せない

通常の場合
exports.joinSpace = functions.https.onCall(async (data, context) => {
  // ...
});
sentryのラップ関数付き
exports.joinSpace = functions.https.onCall(
  Sentry.GCPFunction.wrapEventFunction(async (data, context) => {
    // ...
  })
);
HTTPS callable functionsの呼び出し元
  const joinSpace = functions.httpsCallable("joinSpace");
  await joinSpace()
    .then(function(result) {
      console.log(result.data);
    })
    .catch(error => {
      console.log(error)
    })

フロントエンドから、HTTPS callable functionsを通して、Fucntionsを実行した時に、HTTPS callable functionsの実行完了後にthen()の内容を実行したいのですが、sentryのラップ関数を追加すると、実行完了前にthen()が実行されてしまいます。さらに、Functionsでエラーが発生した時に、catch()の内容が実行されません(エラー時もthen()が実行されます)。

この問題を回避する方法は見つからなかったので、sentryのラップ関数を使うのはやめ、普通にtry catch時にSentryのログ送信を行うように修正しました。

Trigger Background FunctionsでWarningが発生する

Trigger Background Functionsの例
exports.addXX = functions
  .region("asia-northeast1")
  .firestore.document("xxx/{yyy}")
  .onUpdate(
    Sentry.GCPFunction.wrapEventFunction((change, { eventId, params }) => {
      // ..
    })
  );
Warning
{"severity":"WARNING","message":"Function returned undefined, expected Promise or value"}

Trigger Background Functionsでは、処理の最後に値をreturnしないとwarningが出るのですが、sentryのラップ関数を追加すると、値をreturnする方法がなく、Warningが出続けてしまいます。

こちらはWarningは出てしまいますが、そのまま使い続けることにしました。

2022/06/21 追記

sentry serverlessのバージョンを7.1.1にしたところ、

Exception from a finished function: TypeError: callback is not a function

というエラーが出るようになった。

callbackは、FirebaseのCloud Functionsでは指定できないので、やはりsentry serverlessはFirebase Cloud Functionsで使うには適していない。Sentry.GCPFunction.wrapEventFunction を使うのをやめにした。

参考URL:
https://docs.sentry.io/platforms/node/guides/gcp-functions/
https://stackoverflow.com/questions/57266702/cloud-functions-callback-is-not-a-function

結論: 現状では、PlatformはNodeにして、自分でラップ関数を書いた方が良い

僕は既に、「GCP Cloud Functions (Node)」を導入してしまったし、問題も軽微なので、そのまま「GCP Cloud Functions (Node)」を使い続けますが、新規に導入するのであれば、Platformは、「Node」を導入し、自分でラップ関数を書いた方が良いと思います。僕も時間ができた時に、自分のラップ関数にリファクタリングするかもしれません。

最後に

もし、発生した問題を回避する方法をご存知な方がいれば、コメントいただければ幸いです。

今後もVue.js、Firebase関連の記事を書いていく予定なので、良ければフォローお願いいたします!

参考リンク

@sentry/serverlessでServerless Functionをらくらくエラー監視

Discussion