Nuxt.jsでSSR時に起きたエラーをStackdriver Error Reportingでログ収集する

ハゲワシ
8 min readApr 14, 2020

TL;DR

  • Nuxt.jsのhookで使えるrequestオブジェクトに、headerというメソッドを生やせばリクエストパラメータもStackdriverに送れるようになる

はじめに

Nuxt.jsを使ったサービス事例も増えてきてリファレンスが充実した結果、スタートアップのプロジェクトでNuxt.jsを採用するのも難しくなくなってきた。

インフラはクラウド上でk8s、フロントエンドはNuxt.jsでSSR(1コンテナ)×SPA・・・みたいな構成も当たり前になってきたように感じる。

ただSSRとなると、ロギングなどの周辺環境の充実とまでは及んでいないようで、ある程度知識や力が必要になってくると感じている。

GCPを使ってインフラ構築している場合、ログ収集にStackdriver Error Reportingを使うのが一般的だろう。できればNuxt.jsでSSRしている時のエラーログもStackdriverに送りつけたい。

今回はそこで困ったことと解決策を提示する。

困ったこと

事例がまったくない

そもそもNuxt.jsでStackdriverにエラーログを送るという事例があまりにもなくて公式ドキュメントを読んでやっていくしかない。これ自体はむしろ正しい姿なので良い。ドキュメントを読んでいこう

公式ドキュメントでもNuxt.jsのことについて触れられていない

薄々感づいていたけど公式でNuxt.jsとの連携方法については一切触れられていない。

こちらがドキュメント。

詳細はリポジトリのREADMEを読むべしと誘導される。

お̶そ̶ら̶く̶N̶u̶x̶t̶の̶中̶に̶は̶e̶x̶p̶r̶e̶s̶s̶っ̶ぽ̶い̶何̶か̶が̶い̶る̶気̶が̶す̶る̶の̶で̶、̶一̶番̶近̶い̶e̶x̶p̶r̶e̶s̶s̶と̶の̶連̶携̶方̶法̶が̶例̶で̶示̶さ̶れ̶て̶い̶る̶の̶は̶あ̶り̶が̶た̶い̶。̶

(2020/04/25 追記)expressではなくNuxtが生成しているconnectインスタンスだった。もっと良い@google-cloud/error-reportingの使い方があるかもしれない
https://github.com/nuxt/nuxt.js/blob/a2495a68736f9af96cd25209a948beb1ca665d4d/packages/server/src/server.js#L34

const express = require('express');

const {ErrorReporting} = require('@google-cloud/error-reporting');

// Using ES6 style imports via TypeScript or Babel
// import {ErrorReporting} from '@google-cloud/error-reporting';

// Instantiates a client
const errors = new ErrorReporting();

const app = express();

app.get('/error', (req, res, next) => {
res.send('Something broke!');
next(new Error('Custom error message'));
});

app.get('/exception', () => {
JSON.parse('{\"malformedJson\": true');
});

// Note that express error handling middleware should be attached after all
// the other routes and use() calls. See [express docs][express-error-docs].
app.use(errors.express);

app.listen(3000);

なんかいける気がする。

こんな感じでNuxtに組み込んでみた。

  • ~/module/error.ts

こんな感じでhookしたらいけた。

無事にStackdriverにエラーが送られていた。

リクエスト情報が出ない

無事にエラーが送られてこれにて完了。というわけにはいかなかった。

検証してみてすぐに気づいたのだが、全くリクエスト情報が出ないのだ。

正直エラーの文字列だけログが取れたところであまり意味がない。エラーが起きてることが知れるのは良いことだが

色々調べるとやはり普通はリクエスト情報が閲覧できるようだ。requestオブジェクトもしっかり送ってるし使ってないわけがない。

多分使い方がおかしいんだろう。

しかしながら事例がない以上打つ手もない。ということで@google-cloud/error-reportingとnuxtのソースコードリーディングとデバッグをした。

結果、@google-cloud/error-reportingのこの部分でrequestオブジェクトやresponseオブジェクトに関するガード判定をしていて、そのガードに引っかかってしまったためリクエスト情報が収集できなかったようだ。

if (!is.object(req) || !is.function(req.header) || !is.object(res)) {
return returnObject;
}

reqオブジェクトはちゃんとオブジェクトになっているし、rewオブジェクトもちゃんとオブジェクトになっていた。

問題はreq.headerメソッドである。どうやらNuxt.jsのrequestオブジェクトにはheaderメソッドがないようで、この部分のガードに引っかかるようになっていた。

ちなみにreq.header()って何する機能なのって思ったので公式リファレンス見たら、req.get()のaliasらしい。いつか無くなるんじゃね?

Aliased as req.header(field).

このすぐ下の処理で req.header('user-agent') みたいにリクエストヘッダから欲しいパラメータを取ったりしているので、headerメソッドの有無を検証しているのだろう。この処理で取得してる情報が一番大事なのでなんとしても通してあげたい。

というわけで、Nuxt.jsが生成するrequestオブジェクトにheaderメソッドを生やしてあげて、本家expressのそれと同じような機能を果たせるようにした。

app.use((error, req, res, next) => {
type IncomingMessage = typeof req
const newReq = Object.assign(req, {
header(this: IncomingMessage, key: string) {
return this.headers[key]
}
})
errors.express(error, newReq, res, next)
})

こんな感じで、とりあえず動けば良し。

最終的なコードはこうなる。

これで無事にリクエスト情報が表示されるようになった。

まとめ

  • Nuxt.js×Stackdriverの事例が少ない
  • expressの連携方法で概ねいける
  • Nuxt.jsのrequestオブジェクトにheaderメソッドを生やせば完璧

これ実装したの半年以上前なので今はもう事例あるかなぁって調べたけどやっぱりなかった。

是非誰かの参考になることを祈っている。

--

--