JavaScript Tips | ログ・デバッグ・エラー処理:エラーをサーバーへ送信する

JavaScript JavaScript
スポンサーリンク
スポンサーリンク

エラーをサーバーへ送信することは「障害を確実に記録し、運用チームへ“見える化”するための必須技術」

業務システムでは、エラーをサーバーへ送信する仕組みが必ず必要になります。 理由は明確で、次のような問題を防ぐためです。

  • クライアント側だけのログでは本番障害を再現できない
  • ユーザー環境で発生したエラーを開発者が把握できない
  • 監視ツール(Sentry、Datadog、CloudWatch 等)と連携できない
  • 障害の傾向分析ができない
  • 重大エラーをリアルタイムで検知できない

つまり、エラー送信は 「本番運用の生命線」 です。

ここでは、初心者でも理解しやすいように、 エラー整形 → JSON 化 → 送信 → 再送制御 → 業務テンプレート までをステップバイステップで解説します。

エラー送信の目的は「開発者が本番環境の障害を正確に把握すること」

エラー送信は次の情報を含むべきです。

  • エラーコード(分類)
  • エラーメッセージ(概要)
  • スタックトレース(原因特定の最重要情報)
  • 追加情報(APIレスポンス、入力値、環境情報など)
  • 発生コンテキスト(どの処理で起きたか)
  • タイムスタンプ
  • ユーザー環境情報(ブラウザ、OS、URL など)

これらをまとめて送信することで、 障害調査のスピードが圧倒的に向上します。

ステップ1:送信するエラー情報を整形する(最重要)

まずはエラーを構造化します。

function formatErrorForServer(error, context = "") {
  return {
    timestamp: new Date().toISOString(),
    context,
    code: error.code || "SYS-001",
    name: error.name,
    message: error.message,
    stack: error.stack,
    extra: Object.keys(error).reduce((acc, key) => {
      if (!["code", "name", "message", "stack"].includes(key)) {
        acc[key] = error[key];
      }
      return acc;
    }, {}),
    env: {
      url: location.href,
      userAgent: navigator.userAgent,
    },
  };
}
JavaScript

ステップ2:エラーを JSON 化する(送信のための必須処理)

function stringifyError(errorObj) {
  try {
    return JSON.stringify(errorObj);
  } catch {
    return JSON.stringify({ message: "Unserializable error" });
  }
}
JavaScript

ステップ3:サーバーへ送信する関数を作る(基本版)

async function sendErrorToServer(errorObj) {
  await fetch("/error/report", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: stringifyError(errorObj),
  });
}
JavaScript

ステップ4:送信処理を try/catch で安全化する(重要)

エラー送信自体が失敗する可能性があるため、 送信処理も安全に実行します。

async function safeSendError(errorObj) {
  try {
    await sendErrorToServer(errorObj);
  } catch (e) {
    console.warn("エラー送信に失敗しました:", e);
  }
}
JavaScript

ステップ5:エラー発生時に自動送信するユーティリティを作る(完成版)

export const ErrorReporter = {
  async report(error, context = "") {
    const formatted = formatErrorForServer(error, context);
    await safeSendError(formatted);
  }
};
JavaScript

ステップ6:グローバルエラーと組み合わせて自動送信する

ブラウザ

window.onerror = (message, source, lineno, colno, error) => {
  ErrorReporter.report(error || new Error(message), "GlobalError");
};

window.onunhandledrejection = (event) => {
  ErrorReporter.report(event.reason, "UnhandledRejection");
};
JavaScript

Node.js

process.on("uncaughtException", (error) => {
  ErrorReporter.report(error, "UncaughtException");
});

process.on("unhandledRejection", (reason) => {
  ErrorReporter.report(reason, "UnhandledRejection");
});
JavaScript

ステップ7:再送防止(同じエラーを大量送信しないための対策)

本番では同じエラーが大量発生することがあります。 そのため、一定時間内の重複送信を防ぎます。

const sentErrors = new Set();

function shouldSend(errorObj) {
  const key = errorObj.code + errorObj.message;
  if (sentErrors.has(key)) return false;

  sentErrors.add(key);
  setTimeout(() => sentErrors.delete(key), 5000); // 5秒で解除
  return true;
}
JavaScript

送信前にチェックします。

async function safeSendError(errorObj) {
  if (!shouldSend(errorObj)) return;

  try {
    await sendErrorToServer(errorObj);
  } catch (e) {
    console.warn("エラー送信に失敗しました:", e);
  }
}
JavaScript

ステップ8:業務で使えるエラー送信ユーティリティ(完成版)

export const ErrorReporter = {
  format(error, context = "") {
    return {
      timestamp: new Date().toISOString(),
      context,
      code: error.code || "SYS-001",
      name: error.name,
      message: error.message,
      stack: error.stack,
      extra: Object.keys(error).reduce((acc, key) => {
        if (!["code", "name", "message", "stack"].includes(key)) {
          acc[key] = error[key];
        }
        return acc;
      }, {}),
      env: {
        url: location.href,
        userAgent: navigator.userAgent,
      },
    };
  },

  async send(errorObj) {
    try {
      await fetch("/error/report", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: stringifyError(errorObj),
      });
    } catch (e) {
      console.warn("エラー送信に失敗しました:", e);
    }
  },

  async report(error, context = "") {
    const formatted = this.format(error, context);
    if (shouldSend(formatted)) {
      await this.send(formatted);
    }
  },
};
JavaScript

ステップ9:業務での活用例

API 呼び出しでの障害送信

try {
  await fetch("/api/user");
} catch (e) {
  ErrorReporter.report(e, "UserAPI");
}
JavaScript

バッチ処理の異常終了

try {
  runBatch();
} catch (e) {
  ErrorReporter.report(e, "SalesBatch");
}
JavaScript

グローバルエラー送信

ErrorReporter.report(error, "GlobalError");
JavaScript

深掘り:なぜエラーをサーバーへ送信する必要があるのか

1. 本番環境の障害は再現できないことが多い

ユーザー環境のブラウザ・OS・ネットワーク状況は再現困難です。

2. ローカルログだけでは原因特定が不可能

本番で起きたエラーは、開発者の PC では再現できません。

3. 監視ツールと連携するための基盤になる

Sentry、Datadog、CloudWatch などはサーバー送信が前提です。

4. 障害の傾向分析ができる

どのエラーが多いか、どの機能で発生しているかを分析できます。

5. 重大障害をリアルタイムで検知できる

サーバー側で通知を飛ばすことができます。

まとめ:エラー送信は「本番運用の生命線」

エラー送信を正しく設計することで、次のメリットが得られます。

  • 本番環境の障害を確実に記録できる
  • 原因特定が高速化する
  • 監視ツールとの連携が容易になる
  • 障害の傾向分析ができる
  • 重大障害をリアルタイムで検知できる

エラー整形・JSON 化・グローバルエラー捕捉と組み合わせることで、 業務システムの品質が劇的に向上します。

タイトルとURLをコピーしました