Open9

Firebase Cloud FunctionsにおいてthrowされたErrorとcatchしたErrorをそれぞれSlack通知する

やりたいこと

Functionsのエラーログについて、Slack等で検知できるようにする

現状

Functionsのコンソールで見れるけど

  • 通知がない
  • 全部レベルがDebugなので、紛れる
  • UI的に使いにくい

方針

  • このままだと通知がないので、別のログにExportできないか?
  • Debugレベルのログばかりだけど、この情報レベルをコントロールできないか?実装をどのように変えるか?

参考文献

https://firebase.google.com/docs/functions/writing-and-viewing-logs

console.logの利用について

console.logを使ってもいいけど、長期的にはよくない?

console.log を使用していて、Node.js 8 ランタイムから Node.js 10 または 12 に移行すると、ログの読み取りに問題が生じることがあります。この問題を一時的に回避するには、次のライブラリを関数に追加します。

require("firebase-functions/lib/logger/compat");

Node.js 10 または 12 に移行する際に推奨される長期的なソリューションは、ロガー SDK を使用するようにコードをリファクタリングすることです。

現状わかっていること

console.logを使う

→Functionsのログに流れる。レベルは指定されない(ここがDocと違うので混乱)

console.warn/errorを使う

→これらもレベルは指定されない。謎

Functionsのログを他で見る方法

https://console.cloud.google.com/logs/
こちらで見れる
こっちのほうが見た目が綺麗。個人的には好み

throw new Errorする

https://console.cloud.google.com/errors
こちらで見れる
ログではなくてエラー報告。ロギングと、エラー報告の責務を分けることができそう。好感が持てる

これから調べること

Error Reportingから、Slack通知やメール通知ができるか?できるなら、プロダクトコードを改修してエラー時は素直にThrowする。独自例外を作っていく感じかな

Logsから、Slack通知やメール通知ができるか?この場合、大量のログからフィルタリングしないといけないのでちょっと嫌かな。コードを変更したとしても独自例外Throwのほうがよさそう

気が付きやすさを考えるとちょっと手間でもSlack対応するほうがよさそう

→Functionsのログに流れる。レベルは指定されない(ここがDocと違うので混乱)

これが割と致命的で、上記のSlack通知作戦を真似するのであればSeverityで区別できるのがベスト。

ロガー SDKとやらを使えば改善されるのか。試す

ロガーSDKを使っても改善されなかった。要はFirebase Functionsからコンソールに流したログはSeverityがつかないらしい。なんでやねん

ErrorをThrowする→GCPのロギングで見れる→上記の記事を使ってPubSub経由で再度Functionsに持ってきてSlack通知

これがよさそう。

まとめ

GCPのロギングにあるログルーターという機能を使って、Functionsの機能をPubSubに飛ばす
PubSubにさえあればそれをフックにFunctionsを動かすことができるので、Slack通知をやる

ログルーターの使い方

コンソールで適当にポチポチしたら使える。そもそもログエクスプローラーでクエリ言語の練習はできる。ので、そのクエリ言語をフィルタの条件に打ち込めばいい。

Errorがthrowされた場合のログルーター

あえてthrowしたときとか、全く捕捉されなかったとき。

このときはSeverityがERRORでロギングに出るので以下でフィルタリングできる。

resource.type="cloud_function"
resource.labels.project_id="XXXX"
severity>="ERROR"

Errorをキャッチしたが、運営に通知したいときのログルーター

Functions内でconsole.XXXするとSeverityがDEFAULTになってしまうので、以下のように対策した

reportErrorLogという関数を作る

export type LoggerParams = {
  error: Error,
  message: string,
  userId: string,
  logLevel: LogSeverity | 'DEFAULT'
}

export const reportErrorLog = ({ error, userId, logLevel, message }: LoggerParams): void => {
  console.warn(JSON.stringify({
    logLevel,
    userId,
    message: `${message} : ${error.message}`,
  }))
  console.debug(error.stack)
}

JSON.stringifyするのが個人的にポイント

ログルーターの設定

以下のようにする

resource.type="cloud_function"
resource.labels.project_id="XXXX"
severity>="ERROR" OR (severity="DEFAULT" AND jsonPayload.logLevel="ERROR")

severity="DEFAULT" AND jsonPayload.logLevel="ERROR"
が肝。JSON文字列でログを流すとjsonPayloadにログ本文が入るので、そこからlogLevelを抜き取る。これが上記ソースのLoggerParams型と一致しており、ソース上でERRORレベルに設定されたログはSlackに流す、というようにコントロール可能となる

PubSubハンドラ

ログルーターを設定して、PubSubトピックが作れたら、今度はハンドラを実装する

export default () => {
  return functions.pubsub
    .topic('XXX')
    .onPublish(async (pubSubEvent: Message) => {
      const dataString = Buffer.from(pubSubEvent.data, 'base64').toString();
      const data = JSON.parse(dataString) as Data;

      await send(buildBody(data));
    });
};

他のメソッドは省略するがこんな感じ。


以上で、Firebase Cloud FunctionsにおいてthrowされたErrorとcatchしたErrorをそれぞれSlack通知することが実現できた。

ログインするとコメントできます