MENU

GAS×エラー通知|Slack・メールで自動アラートを実現する方法

GAS×エラー通知|Slack・メールで自動アラートを実現する方法

GASで自動化したスクリプトは、動いている間は快適だが、エラーで止まっていても気づきにくい。日次バッチが3日前から止まっていた、という事態は珍しくない。

エラー発生時にSlackやメールへ自動通知する仕組みを組み込んでおけば、そうした見落としを防げる。ここではコピペで使えるコード付きで、実装手順を説明していく。


目次

エラー通知を入れる理由

自動化が抱える落とし穴

GASによる自動化は便利だが、以下のような問題がつきまとう。

問題 具体例 影響
サイレントエラー APIの仕様変更で処理が失敗 データ欠損に気づかない
タイムアウト 大量データ処理で6分超過 途中で処理が止まる
権限エラー 共有設定の変更でアクセス不可 他メンバーの作業に影響
API制限 短時間に大量リクエスト 一定期間使用不可

問題の核心は、エラーの発見が遅れることにある。通知の仕組みを入れておけば、発生から数秒で把握できるし、Slack通知ならチーム全体で共有もできる。


try-catchでエラーを捕捉する

基本構文

GASでエラーを捕捉するにはJavaScriptのtry-catch構文を使う。


function basicErrorHandling() {
  try {
    // エラーが発生する可能性のある処理
    const result = riskyOperation();
    console.log('処理成功:', result);
  } catch (error) {
    // エラーが発生したときの処理
    console.error('エラー発生:', error.message);
  } finally {
    // エラーの有無に関わらず実行される処理
    console.log('処理終了');
  }
}

エラー情報の取得

errorオブジェクトからは以下の情報を取り出せる。


function getErrorDetails(error) {
  return {
    message: error.message,      // エラーメッセージ
    name: error.name,            // エラーの種類
    stack: error.stack,          // スタックトレース
    fileName: error.fileName,    // ファイル名(あれば)
    lineNumber: error.lineNumber // 行番号(あれば)
  };
}

メールでエラー通知を送る

【コード①】シンプルなメール通知

まずは最小限の構成から。


/**
 * エラー発生時にメールで通知する
 * @param {Error} error - 発生したエラー
 * @param {string} scriptName - スクリプト名
 */
function sendErrorEmail(error, scriptName) {
  const recipient = 'your-email@example.com'; // 通知先メールアドレス
  const subject = `【エラー通知】${scriptName}でエラーが発生しました`;

  const body = `
以下のエラーが発生しました。

■ スクリプト名
${scriptName}

■ 発生日時
${Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss')}

■ エラーメッセージ
${error.message}

■ スタックトレース
${error.stack || 'なし'}

■ 対応
スクリプトエディタを確認し、エラーの原因を調査してください。
`;

  MailApp.sendEmail(recipient, subject, body);
}

// 使用例
function mainProcess() {
  const SCRIPT_NAME = '日次データ集計';

  try {
    // メイン処理
    processData();
  } catch (error) {
    sendErrorEmail(error, SCRIPT_NAME);
    throw error; // 必要に応じて再スロー
  }
}

メール通知で押さえる点

  • 件名に【エラー通知】を付けておくとフィルタ設定しやすい
  • 発生日時は必ず記載する。いつ起きたか一目で分かる
  • スタックトレースを含めるとデバッグが捗る

Slack Webhookでエラー通知を送る

Slack Webhook URLの取得

  • Slack APIにアクセス
  • 「Create New App」→「From scratch」
  • 「Incoming Webhooks」を有効化
  • 「Add New Webhook to Workspace」でチャンネル選択
  • 生成されたWebhook URLをコピー

【コード②】Slack通知(基本版)


/**
 * Slackにエラー通知を送信する
 * @param {Error} error - 発生したエラー
 * @param {string} scriptName - スクリプト名
 */
function sendSlackErrorNotification(error, scriptName) {
  const WEBHOOK_URL = 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL';

  const payload = {
    username: 'GAS Error Bot',
    icon_emoji: ':warning:',
    attachments: [{
      color: 'danger',
      title: `🚨 ${scriptName} でエラーが発生`,
      fields: [
        {
          title: '発生日時',
          value: Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'),
          short: true
        },
        {
          title: 'エラー種別',
          value: error.name || 'Error',
          short: true
        },
        {
          title: 'エラーメッセージ',
          value: error.message,
          short: false
        },
        {
          title: 'スタックトレース',
          value: '```' + (error.stack || 'なし') + '```',
          short: false
        }
      ],
      footer: 'GAS Error Notification',
      ts: Math.floor(Date.now() / 1000)
    }]
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  UrlFetchApp.fetch(WEBHOOK_URL, options);
}

エラーレベル別の通知設計

すべてのエラーを同列に扱うと、通知が来ても緊急度が分からない。レベルを分けて対応を変える。


/**
 * エラーレベルの定義
 */
const ERROR_LEVELS = {
  CRITICAL: {
    color: '#FF0000',
    emoji: ':rotating_light:',
    mention: '<!channel>',  // チャンネル全員に通知
    description: '即時対応が必要'
  },
  WARNING: {
    color: '#FFA500',
    emoji: ':warning:',
    mention: '',
    description: '確認が必要'
  },
  INFO: {
    color: '#0000FF',
    emoji: ':information_source:',
    mention: '',
    description: '参考情報'
  }
};

/**
 * エラーレベルに応じたSlack通知
 */
function sendLeveledSlackNotification(error, scriptName, level = 'WARNING') {
  const WEBHOOK_URL = 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL';
  const config = ERROR_LEVELS[level];

  const payload = {
    text: config.mention,
    username: 'GAS Alert System',
    icon_emoji: config.emoji,
    attachments: [{
      color: config.color,
      title: `${config.emoji} [${level}] ${scriptName}`,
      text: config.description,
      fields: [
        {
          title: '発生日時',
          value: Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'),
          short: true
        },
        {
          title: 'エラーメッセージ',
          value: error.message,
          short: false
        }
      ]
    }]
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload)
  };

  UrlFetchApp.fetch(WEBHOOK_URL, options);
}

// 使用例
try {
  criticalProcess();
} catch (error) {
  sendLeveledSlackNotification(error, 'データ同期処理', 'CRITICAL');
}

実践例① 日次バッチのエラー監視

日次で走るバッチ処理に、エラー監視を組み込む例。

【コード③】日次バッチ監視システム


/**
 * 日次バッチ処理のラッパー
 * エラー発生時に自動通知 + ログ記録
 */
function dailyBatchWithMonitoring() {
  const SCRIPT_NAME = '日次売上集計バッチ';
  const startTime = new Date();
  let status = 'SUCCESS';
  let errorInfo = null;

  try {
    console.log(`[${SCRIPT_NAME}] 処理開始: ${startTime}`);

    // ===== メイン処理 =====
    step1_fetchData();
    step2_processData();
    step3_updateSheet();
    step4_generateReport();
    // =====================

    console.log(`[${SCRIPT_NAME}] 処理完了`);

  } catch (error) {
    status = 'FAILED';
    errorInfo = error;

    // エラー通知(Slack + メール)
    sendSlackErrorNotification(error, SCRIPT_NAME);
    sendErrorEmail(error, SCRIPT_NAME);

    console.error(`[${SCRIPT_NAME}] エラー発生: ${error.message}`);

  } finally {
    // 実行ログを記録
    logExecutionResult(SCRIPT_NAME, startTime, status, errorInfo);
  }
}

/**
 * 実行結果をスプレッドシートに記録
 */
function logExecutionResult(scriptName, startTime, status, error) {
  const LOG_SHEET_ID = 'YOUR_LOG_SHEET_ID';
  const sheet = SpreadsheetApp.openById(LOG_SHEET_ID).getSheetByName('実行ログ');

  const endTime = new Date();
  const duration = (endTime - startTime) / 1000; // 秒

  sheet.appendRow([
    Utilities.formatDate(startTime, 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'),
    scriptName,
    status,
    duration + '秒',
    error ? error.message : '-'
  ]);
}

実践例② トリガー実行のエラーログ

トリガーで実行されるスクリプトは、エラーが起きても画面に表示されない。すべてのトリガー実行をログに残す仕組みを作っておくと安心できる。


/**
 * トリガー実行用のエラーハンドリングラッパー
 * @param {Function} mainFunction - メイン処理関数
 * @param {string} functionName - 関数名(ログ用)
 */
function triggerWrapper(mainFunction, functionName) {
  const executionId = Utilities.getUuid();

  try {
    console.log(`[${executionId}] ${functionName} 開始`);

    mainFunction();

    console.log(`[${executionId}] ${functionName} 正常終了`);

    // 成功時もログに記録(オプション)
    logToSheet(executionId, functionName, 'SUCCESS', null);

  } catch (error) {
    console.error(`[${executionId}] ${functionName} エラー: ${error.message}`);

    // エラーログ記録
    logToSheet(executionId, functionName, 'ERROR', error);

    // 通知送信
    sendSlackErrorNotification(error, functionName);

    // エラーを再スローしない(トリガーの連続失敗を防ぐ)
  }
}

/**
 * スプレッドシートにログを記録
 */
function logToSheet(executionId, functionName, status, error) {
  const props = PropertiesService.getScriptProperties();
  const LOG_SHEET_ID = props.getProperty('LOG_SHEET_ID');

  if (!LOG_SHEET_ID) return;

  const sheet = SpreadsheetApp.openById(LOG_SHEET_ID).getSheetByName('トリガーログ');

  sheet.appendRow([
    new Date(),
    executionId,
    functionName,
    status,
    error ? error.message : '-',
    error ? error.stack : '-'
  ]);
}

// トリガーに設定する関数
function scheduledTask() {
  triggerWrapper(actualTask, 'scheduledTask');
}

function actualTask() {
  // 実際の処理をここに書く
  const data = fetchExternalData();
  processAndSave(data);
}

エラー通知のベストプラクティス

1. 通知先の使い分け

状況 推奨通知先 理由
即時対応が必要 Slack(チャンネルメンション) すぐに気づける
日次確認で十分 メール or Slack(通常) 通知疲れを防ぐ
ログとして残したい スプレッドシート 後から検索・分析可能
複数人で対応 Slack + メンション チームで共有

2. 通知内容に含めるべき情報


✅ 必須項目
- スクリプト名
- 発生日時
- エラーメッセージ
- エラーの種類

✅ あると便利
- スタックトレース
- 実行ID(複数ログの紐付け用)
- 影響範囲
- 復旧手順へのリンク

3. 通知疲れを防ぐ

同じエラーが連続発生すると、通知が大量に飛ぶ。スロットリングを入れて抑制する。


/**
 * 同一エラーの連続通知を抑制
 */
function sendNotificationWithThrottle(error, scriptName) {
  const cache = CacheService.getScriptCache();
  const cacheKey = `error_${scriptName}_${error.message}`.slice(0, 250);

  // 過去5分以内に同じエラーを通知済みかチェック
  if (cache.get(cacheKey)) {
    console.log('同一エラーの通知を抑制');
    return;
  }

  // 通知送信
  sendSlackErrorNotification(error, scriptName);

  // 5分間キャッシュ(同じエラーの再通知を防ぐ)
  cache.put(cacheKey, 'sent', 300);
}

4. 復旧手順を用意しておく

エラーが起きたときにパニックにならないよう、主要なエラーへの対応手順をドキュメント化しておくとよい。


// エラー別の対応手順リンクを通知に含める
const ERROR_RECOVERY_DOCS = {
  'Exceeded maximum execution time': 'https://your-docs/timeout-recovery',
  'Service invoked too many times': 'https://your-docs/quota-recovery',
  'Access denied': 'https://your-docs/permission-recovery'
};

function getRecoveryDoc(errorMessage) {
  for (const [pattern, url] of Object.entries(ERROR_RECOVERY_DOCS)) {
    if (errorMessage.includes(pattern)) {
      return url;
    }
  }
  return 'https://your-docs/general-troubleshooting';
}

まとめ

導入のステップ


Step 1: 既存スクリプトにtry-catchを追加
Step 2: sendErrorEmail関数を追加してテスト
Step 3: Slack Webhookを設定
Step 4: エラーレベル別の通知を実装
Step 5: 通知スロットリングで最適化

try-catchで捕捉し、メールかSlackで飛ばし、スプレッドシートにログを残す。この3段構えでGASの自動化は格段に安定する。エラー通知の有無が、運用の信頼性を分ける。


※ この記事のコードはそのままコピペで動作しますが、WEBHOOK_URLやメールアドレスは必ずご自身のものに変更してください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次