Firebase Cloud FunctionsにおいてthrowされたErrorとcatchしたErrorをそれぞれSlack通知する
やりたいこと
Functionsのエラーログについて、Slack等で検知できるようにする
現状
Functionsのコンソールで見れるけど
- 通知がない
- 全部レベルがDebugなので、紛れる
- UI的に使いにくい
方針
- このままだと通知がないので、別のログにExportできないか?
- Debugレベルのログばかりだけど、この情報レベルをコントロールできないか?実装をどのように変えるか?
参考文献
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のログを他で見る方法
こっちのほうが見た目が綺麗。個人的には好み
throw new Errorする
ログではなくてエラー報告。ロギングと、エラー報告の責務を分けることができそう。好感が持てる
これから調べること
Error Reportingから、Slack通知やメール通知ができるか?できるなら、プロダクトコードを改修してエラー時は素直にThrowする。独自例外を作っていく感じかな
Logsから、Slack通知やメール通知ができるか?この場合、大量のログからフィルタリングしないといけないのでちょっと嫌かな。コードを変更したとしても独自例外Throwのほうがよさそう
Error Reporting to Slack
ここによるとモバイルアプリやメールはあるけどSlackはないらしい。つらい
気が付きやすさを考えるとちょっと手間でも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通知することが実現できた。