🐛

Next.js AppRouterのエラートラッキング・ロギング手法で悩む

2024/02/22に公開

Next.js AppRouterのエラートラッキングで悩む

前提条件

  • Next.js v14.1
  • App Router
  • セリフホスティング(AWS ECS)

僕が調べた限りでは現状(2024/02/20)では、Next.js AppRouterのすべてのエラーやリクエストログを、自由に使いやすくトラッキングやロギングする方法はなさそうです。主に以下のような壁にぶつかりました。

エラーやリクエストログが自由に取れない…

Server Componentのエラーログを自由に取れない問題

  • Server Componentの例外をサーバ側で計測できない
    • 自動で error.tsx のClient Componentでキャッチされる仕様
  • error.tsx で手に入るサーバ側のエラー詳細情報は隠蔽されている
    • 機密情報がブラウザ側に漏洩しないようにNext.jsが配慮してくれるため
  • サーバ側のエラーログは標準出力されるので、CloudWatch LogsやDatadogで取れる
    • ただし改行されているので、改行ごとに1レコード扱いになり実用に耐えない
    • ハンドリングできればJSONなどで構造化できるのだが…


このように複数行に分割されてまとめて見られない

レスポンスに関する情報を自由に取れない問題

  • リクエスト情報は middleware.ts の中でロギングすることができる
    • ただし、実際にレスポンスした情報はまだMiddleware時点ではとれない
    • 例えばHTTPステータスコードやレスポンス時間など
  • Vercelにデプロイしたら、Vercel Logには出力されるので何らかのやり方はありそう
Vercelにデプロイした例
レスポンスタイムとか取りたい ステータスコードも取りたい

なお、middleware.ts でリクエストログを出力してDatadogに構造化してもらうデモコードはこんな感じ。console.logでオブジェクトを文字列として出力している(そうすると1レコードにまとめてくれる)。

import { type NextRequest, NextResponse, userAgent } from "next/server";
import {
  getSession,
  withMiddlewareAuthRequired,
} from "@auth0/nextjs-auth0/edge";

export default async function middleware(
  request: NextRequest,
  event: NextFetchEvent,
) {
  const response = NextResponse.next();
  const { pathname } = request.nextUrl;
  const { pathname, searchParams } = request.nextUrl;
  const agent = userAgent(request);

  // Auth0のログイン情報取得
  const session = await getSession(request, response);

  const log: ErrorType = {
    level: "info",
    // ALBからリクエストIPアドレスをもらう
    ip: request.ip ?? request.headers.get("x-forwarded-for") ?? "",
    method: request.method,
    pathname: pathname,
    search_params: Object.fromEntries(searchParams),
    browser: agent.browser,
    device: agent.device,
    "user-agent": request.headers.get("user-agent"),
    // ALBからトレース可能なリクエストIDをもらう
    "x-amzn-trace-id": request.headers.get("x-amzn-trace-id"),
  };
  if (session) {
    log.user = {
      app: session.user.app,
      sid: session.user.sid,
      sub: session.user.sub,
      email: session.user.email,
      name: session.user.name,
    };
  }
  // AWS Firelensを通してDatadogにレポートする
  console.log(JSON.stringify(log));

  return response;
}

エラートラッキングが完全にできない問題

  • エラートラッキング手法は様々あるが、SentryやRollbar・Datadogでは完全に取れない
    • GitHubの様子を見ていると、これは時間の問題でいつか対応される気がする
    • 完全に取れないのは主にServer Componentに関するもの
      • Client Componentのブラウザ側は問題なく取れる
  • 代わりに自前でトラッキングしようとしても、前述したServer Componentのエラー詳細が取れない
    • try..catch してレポートしても、リクエスト情報を自前で集めて構造化する必要がある
      • めちゃ面倒くさい…
  • Sentryの場合、Server Actionsのエラートラッキングするためには独自の Sentry.withServerActionInstrumentation 関数でラップする必要がある
    • 実装の中にエラートラッキングに関する部分が出すぎていてなんか嫌…
  • bugpilotというトラッキングサービスが一応、すべてのエラーをレポートすることができる
    • ただ、新しいサービスのため完成度は他に劣る・海外にリージョンだけなのでレスポンスが悪い
    • Next.js中心に改善していく感じっぽいので、気になる人は使ってみると良い
      • トライアルもある
    • ちなみにbugpilotがすべてのエラーを取れる理由は、Webpack pluginを提供していて、ビルド時にServer ActionsやServer Componentを自動でwrapして計測できるようにしているからです
      • Turbopackに移ったら、その対応もしないといけないので大変そうですが…現状だと仕方がないですね

悩みは解決していない

という感じで、部分的にはエラートラッキングもロギングもできるけれど自由に簡単に完全に取ることができない状態です。今のところは運用でなんとかできるので対応が進むのを期待してもう寝ます。

ムーザルちゃんねる

Discussion