エラーをログサーバーに送信する|JavaScript
本番環境ではエラーが発生しても開発者のコンソールには表示されません。ユーザーの環境で起きたエラーを把握するには、エラー情報をサーバーに送信する仕組みが必要です。
基本的な送信方法
fetch を使ってエラー情報を JSON で送信します。
function reportError(error) {
const payload = {
message: error.message,
stack: error.stack,
url: location.href,
userAgent: navigator.userAgent,
timestamp: new Date().toISOString()
};
fetch('/api/errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
}).catch(() => {
// エラー送信自体が失敗しても握りつぶす
});
}エラー送信の失敗で別のエラーが発生しないよう、catch で握りつぶすのがポイントです。
sendBeacon を使う
ページ遷移やタブを閉じる直前でも確実に送信したい場合は、navigator.sendBeacon を使います。
function reportError(error) {
const payload = JSON.stringify({
message: error.message,
stack: error.stack,
url: location.href
});
navigator.sendBeacon('/api/errors', payload);
}sendBeacon はブラウザがバックグラウンドで送信を完了するため、ユーザー体験を阻害しません。
レスポンスを待てる。細かい制御が可能。ページ離脱時に送信が中断されることがある。
レスポンスは受け取れない。ページ離脱時も確実に送信される。
グローバルエラーハンドラと組み合わせる
window.onerror と unhandledrejection の両方でエラーを捕捉し、送信します。
// 同期エラー
window.onerror = function(message, source, lineno, colno, error) {
reportError({
type: 'onerror',
message,
source,
lineno,
colno,
stack: error?.stack
});
return false;
};
// Promise のエラー
window.addEventListener('unhandledrejection', event => {
reportError({
type: 'unhandledrejection',
message: event.reason?.message,
stack: event.reason?.stack
});
});送信するべき情報
デバッグに役立つ情報をできるだけ含めます。
message, stack, name など Error オブジェクトのプロパティ
URL、行番号、列番号、ソースファイル名
ブラウザ、OS、画面サイズ、言語設定
ログインユーザー ID、セッション ID、直前の操作履歴
function collectErrorContext(error) {
return {
// エラー情報
message: error.message,
stack: error.stack,
name: error.name,
// 発生場所
url: location.href,
referrer: document.referrer,
// 環境情報
userAgent: navigator.userAgent,
language: navigator.language,
screenSize: `${screen.width}x${screen.height}`,
viewportSize: `${window.innerWidth}x${window.innerHeight}`,
// タイムスタンプ
timestamp: new Date().toISOString(),
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
};
}エラーの重複排除
同じエラーが短時間に大量に送信されるのを防ぎます。
const reportedErrors = new Set();
function reportError(error) {
const key = `${error.message}:${error.stack?.slice(0, 200)}`;
if (reportedErrors.has(key)) {
return; // 既に報告済み
}
reportedErrors.add(key);
// 5分後に再報告可能にする
setTimeout(() => reportedErrors.delete(key), 5 * 60 * 1000);
sendToServer(collectErrorContext(error));
}サンプリング
トラフィックが多いサイトでは、一部のエラーのみを送信するサンプリングを行うこともあります。
function reportError(error) {
// 10% のエラーのみ送信
if (Math.random() > 0.1) {
return;
}
sendToServer(collectErrorContext(error));
}エラー追跡サービスの活用
自前で実装する代わりに、専用のサービスを使う方法もあります。
これらのサービスは、スクリプトを読み込むだけで自動的にエラーを収集し、ダッシュボードで分析できます。ソースマップのアップロードにも対応しており、難読化されたコードのスタックトレースも元のソースに変換されます。
サーバーサイドでの処理
送信されたエラーはサーバーで受け取り、データベースに保存したり、通知を送ったりします。
// Express の例
app.post('/api/errors', express.json(), (req, res) => {
const errorData = req.body;
// データベースに保存
db.errors.insert(errorData);
// 重大なエラーは Slack に通知
if (errorData.message.includes('Critical')) {
notifySlack(errorData);
}
res.status(204).end();
});エラーログを定期的に分析することで、ユーザーが遭遇している問題を把握し、サービスの品質向上につなげられます。