GASデバッグ完全ガイド|初心者がつまずくポイントと解決テクニック
GAS開発では、エラーを自力で解決するデバッグスキルが必須です。この記事では、ログ出力からブレークポイント、エラーハンドリングまで、実践的なデバッグテクニックを体系的に解説します。
目次
デバッグの基本: ログ出力
デバッグの第一歩は「変数の中身を確認すること」です。GASには2つのログ出力方法があります。
Logger.log() vs console.log() の違い
| 項目 | Logger.log() | console.log() |
|---|---|---|
| 出力先 | Apps Script ログ(旧エディタ) | 実行ログ(新エディタ) |
| リアルタイム表示 | 不可(実行後に確認) | 可能 |
| トリガー実行時 | 確認しにくい | Stackdriver Loggingで確認可 |
| 推奨度 | 旧式 | 推奨 |
結論: console.log() を使いましょう。
console.log() の基本的な使い方
function debugBasic() {
// 文字列を出力
console.log('処理を開始します');
// 変数の値を確認
const userName = 'テスト太郎';
console.log('ユーザー名: ' + userName);
// 複数の値を出力(カンマ区切り)
const age = 30;
console.log('名前:', userName, '年齢:', age);
// テンプレートリテラル(バッククォート)で見やすく
console.log(`名前: ${userName}, 年齢: ${age}`);
}
実行結果:
処理を開始します
ユーザー名: テスト太郎
名前: テスト太郎 年齢: 30
名前: テスト太郎, 年齢: 30
オブジェクトと配列のデバッグ
配列やオブジェクトの中身を確認するときは、JSON.stringify() を使います。
function debugObjects() {
// 配列の中身を確認
const fruits = ['りんご', 'バナナ', 'オレンジ'];
console.log('配列そのまま:', fruits); // りんご,バナナ,オレンジ
console.log('JSON形式:', JSON.stringify(fruits)); // ["りんご","バナナ","オレンジ"]
// オブジェクトの中身を確認
const user = {
name: '山田太郎',
email: 'yamada@example.com',
age: 25
};
console.log('オブジェクトそのまま:', user); // [object Object]
console.log('JSON形式:', JSON.stringify(user)); // {"name":"山田太郎",...}
// 見やすく整形(インデント付き)
console.log('整形JSON:', JSON.stringify(user, null, 2));
/*
{
"name": "山田太郎",
"email": "yamada@example.com",
"age": 25
}
*/
}
スプレッドシートデータのデバッグ
function debugSpreadsheetData() {
const sheet = SpreadsheetApp.getActiveSheet();
const data = sheet.getDataRange().getValues();
// データ全体の構造を確認
console.log('行数:', data.length);
console.log('列数:', data[0].length);
// 先頭5行だけ確認(大量データの場合)
console.log('先頭5行:');
for (let i = 0; i < Math.min(5, data.length); i++) {
console.log(` 行${i + 1}: ${JSON.stringify(data[i])}`);
}
// 特定のセルを確認
console.log('A1セル:', data[0][0]);
console.log('B2セル:', data[1][1]);
}
実行ログの見方
ログの確認方法
- スクリプトエディタで関数を実行
- 画面下部の「実行ログ」パネルを確認
- パネルが見えない場合は「表示」→「実行ログ」
ログレベルの使い分け
function logLevels() {
// 通常のログ(情報)
console.log('通常のログメッセージ');
// 情報(console.logと同等)
console.info('情報レベルのログ');
// 警告(黄色で表示)
console.warn('警告: 処理は続行しますが注意が必要です');
// エラー(赤色で表示)
console.error('エラー: 処理を中断しました');
}
使い分けの目安:
| レベル | 用途 | 例 |
|---|---|---|
console.log() |
通常の情報 | 処理開始、変数の値確認 |
console.info() |
重要な情報 | 処理完了、成功メッセージ |
console.warn() |
警告 | 非推奨機能の使用、想定外の値 |
console.error() |
エラー | 処理失敗、例外発生 |
トリガー実行時のログ確認
トリガーで自動実行された処理のログは、通常のエディタからは見えません。
確認方法:
- GASエディタで「実行」→実行数をクリック
- または Google Cloud Console で確認
- 「Stackdriver Logging」→ログで検索
ブレークポイントの使い方
ブレークポイントを使うと、コードの実行を一時停止して、その時点の変数の値を詳しく確認できます。
ブレークポイントの設定
- 停止したい行の行番号の左側をクリック
- 赤い丸(●)が表示される
- 「デバッグ」ボタン(虫のアイコン)をクリック
- ブレークポイントで実行が停止
デバッガーの操作方法
| ボタン | 操作 | 説明 |
|---|---|---|
| ▶️ 再開 | 次のブレークポイントまで実行 | |
| ⏭️ ステップオーバー | 1行実行(関数内には入らない) | |
| ⬇️ ステップイン | 1行実行(関数内に入る) | |
| ⬆️ ステップアウト | 現在の関数を抜ける | |
| ⏹️ 停止 | デバッグを終了 |
デバッグ中に確認できること
function debugWithBreakpoint() {
const data = [
{ name: '商品A', price: 1000 },
{ name: '商品B', price: 2000 },
{ name: '商品C', price: 3000 }
];
let total = 0;
for (let i = 0; i < data.length; i++) {
const item = data[i]; // ← ここにブレークポイント
total += item.price;
}
console.log('合計:', total);
}
ブレークポイントで確認できる情報:
iの現在値(0, 1, 2…)itemの中身({ name: '商品A', price: 1000 })totalの途中経過(0, 1000, 3000…)data配列の全内容
ブレークポイントの条件設定
特定の条件でのみ停止させることもできます。
- ブレークポイントを右クリック
- 「条件付きブレークポイント」を選択
- 条件式を入力(例:
i === 5)
try-catch によるエラーハンドリング
基本構文
function tryCatchBasic() {
try {
// エラーが起きる可能性のある処理
const result = riskyOperation();
console.log('成功:', result);
} catch (error) {
// エラー発生時の処理
console.error('エラー発生:', error.message);
} finally {
// 成功・失敗に関わらず必ず実行
console.log('処理終了');
}
}
エラーオブジェクトの中身
function errorDetails() {
try {
// 意図的にエラーを発生させる
const obj = null;
obj.method(); // TypeError
} catch (error) {
// エラーの詳細情報
console.log('エラー名:', error.name); // TypeError
console.log('メッセージ:', error.message); // Cannot read properties of null
console.log('スタック:', error.stack); // エラー発生箇所の詳細
}
}
try-catch パターン集
パターン1: API呼び出しのエラーハンドリング
/**
* 外部API呼び出し(リトライ付き)
*/
function callAPIWithRetry(url, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`API呼び出し(${attempt}回目)`);
const response = UrlFetchApp.fetch(url, {
muteHttpExceptions: true
});
const code = response.getResponseCode();
if (code === 200) {
return JSON.parse(response.getContentText());
} else if (code === 429) {
// レート制限: 待機してリトライ
console.warn('レート制限。30秒待機します...');
Utilities.sleep(30000);
continue;
} else {
throw new Error(`HTTPエラー: ${code}`);
}
} catch (error) {
console.error(`${attempt}回目失敗: ${error.message}`);
if (attempt === maxRetries) {
throw new Error(`${maxRetries}回リトライしましたが失敗しました: ${error.message}`);
}
// 次のリトライまで待機
Utilities.sleep(5000);
}
}
}
パターン2: スプレッドシート操作のエラーハンドリング
/**
* スプレッドシートを安全に開く
*/
function safeOpenSpreadsheet(spreadsheetId) {
try {
const ss = SpreadsheetApp.openById(spreadsheetId);
console.log('スプレッドシートを開きました:', ss.getName());
return ss;
} catch (error) {
if (error.message.includes('not found') || error.message.includes('missing')) {
console.error('スプレッドシートが見つかりません。削除された可能性があります。');
console.error('ID:', spreadsheetId);
} else if (error.message.includes('permission')) {
console.error('アクセス権限がありません。共有設定を確認してください。');
} else {
console.error('予期しないエラー:', error.message);
}
return null;
}
}
/**
* シートを安全に取得
*/
function safeGetSheet(spreadsheet, sheetName) {
if (!spreadsheet) {
console.error('スプレッドシートがnullです');
return null;
}
const sheet = spreadsheet.getSheetByName(sheetName);
if (!sheet) {
console.error(`シート「${sheetName}」が見つかりません`);
console.log('存在するシート:', spreadsheet.getSheets().map(s => s.getName()));
return null;
}
return sheet;
}
パターン3: 一括処理のエラーハンドリング(スキップ方式)
/**
* 複数行を処理(エラーが起きてもスキップして続行)
*/
function processBatchWithSkip() {
const sheet = SpreadsheetApp.getActiveSheet();
const data = sheet.getDataRange().getValues();
let successCount = 0;
let errorCount = 0;
const errors = [];
for (let i = 1; i < data.length; i++) {
try {
const row = data[i];
// 各行の処理
processRow(row, i + 1);
successCount++;
} catch (error) {
errorCount++;
errors.push({
row: i + 1,
message: error.message
});
console.warn(`行${i + 1}でエラー: ${error.message}(スキップして続行)`);
// continueで次の行へ
}
}
// 処理結果サマリー
console.log('=== 処理完了 ===');
console.log(`成功: ${successCount}件`);
console.log(`失敗: ${errorCount}件`);
if (errors.length > 0) {
console.log('エラー詳細:');
errors.forEach(e => console.log(` 行${e.row}: ${e.message}`));
}
return { successCount, errorCount, errors };
}
パターン4: エラー通知付きテンプレート
/**
* 本番運用向け: エラー通知機能付きメイン処理
*/
function mainWithNotification() {
const startTime = new Date();
const functionName = 'mainWithNotification';
console.log(`[${functionName}] 処理開始: ${startTime.toLocaleString()}`);
try {
// ===== メイン処理 =====
const result = doMainProcess();
// 成功時のログ
console.log(`[${functionName}] 処理成功:`, result);
} catch (error) {
// エラー情報を構造化
const errorInfo = {
functionName: functionName,
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
};
// ログに詳細出力
console.error(`[${functionName}] エラー発生:`, JSON.stringify(errorInfo, null, 2));
// メールで通知
sendErrorNotificationEmail(errorInfo);
// 必要に応じてエラーを再スロー
// throw error;
} finally {
const endTime = new Date();
const duration = (endTime - startTime) / 1000;
console.log(`[${functionName}] 実行時間: ${duration}秒`);
}
}
/**
* エラー通知メール送信
*/
function sendErrorNotificationEmail(errorInfo) {
const recipient = Session.getActiveUser().getEmail();
const subject = `【GASエラー】${errorInfo.functionName} でエラーが発生しました`;
const body = `
GASスクリプトでエラーが発生しました。
■ 関数名
${errorInfo.functionName}
■ 発生日時
${errorInfo.timestamp}
■ エラーメッセージ
${errorInfo.message}
■ スタックトレース
${errorInfo.stack}
---
このメールは自動送信されています。
`.trim();
try {
GmailApp.sendEmail(recipient, subject, body);
console.log('エラー通知メールを送信しました');
} catch (e) {
console.error('エラー通知メールの送信に失敗:', e.message);
}
}
権限エラーの対処法
よくある権限エラー
Exception: You do not have permission to call SpreadsheetApp.openById
対処法1: 権限の再承認
- スクリプトエディタで関数を手動実行
- 「承認が必要です」ダイアログで「許可を確認」
- Googleアカウントを選択
- 「詳細」→「(プロジェクト名)に移動」
- 許可をクリック
対処法2: マニフェストで明示的に指定
appsscript.json で必要な権限を明示します。
{
"timeZone": "Asia/Tokyo",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/drive"
]
}
主要なスコープ一覧:
| サービス | スコープ |
|---|---|
| スプレッドシート | https://www.googleapis.com/auth/spreadsheets |
| Gmail送信 | https://www.googleapis.com/auth/gmail.send |
| カレンダー | https://www.googleapis.com/auth/calendar |
| ドライブ | https://www.googleapis.com/auth/drive |
| 外部API | https://www.googleapis.com/auth/script.external_request |
対処法3: トリガーの権限確認
トリガー実行時の権限エラーは、トリガーを削除して再作成すると解決することがあります。
- 「トリガー」ページを開く
- 該当トリガーの「︙」→「削除」
- 新しくトリガーを作成
API制限エラーの対処法
よくあるエラー
Exception: Rate Limit Exceeded
Exception: Quota exceeded: Email Recipients per day
GASの主な制限値
| サービス | 無料アカウント | Google Workspace |
|---|---|---|
| スクリプト実行時間 | 6分/回 | 30分/回 |
| トリガー合計実行時間 | 90分/日 | 6時間/日 |
| メール送信 | 100件/日 | 1,500件/日 |
| UrlFetchApp | 20,000回/日 | 100,000回/日 |
対処法1: 待機時間を入れる
function processWithDelay() {
const items = getItems();
for (let i = 0; i < items.length; i++) {
processItem(items[i]);
// 100件ごとに5秒待機
if ((i + 1) % 100 === 0) {
console.log(`${i + 1}件処理完了。5秒待機...`);
Utilities.sleep(5000);
}
}
}
対処法2: クォータ確認
function checkQuotas() {
// メール送信可能数
const emailQuota = MailApp.getRemainingDailyQuota();
console.log(`メール送信可能数: ${emailQuota}件`);
if (emailQuota < 10) {
console.warn('クォータ残りわずか!');
}
}
対処法3: 処理を分割(バッチ処理)
/**
* 大量データを分割して処理
*/
function batchProcess() {
const props = PropertiesService.getScriptProperties();
const startIndex = parseInt(props.getProperty('batchIndex')) || 0;
const allData = getAllData();
const batchSize = 100;
const endIndex = Math.min(startIndex + batchSize, allData.length);
console.log(`処理中: ${startIndex + 1} 〜 ${endIndex} / ${allData.length}`);
for (let i = startIndex; i < endIndex; i++) {
processItem(allData[i]);
}
if (endIndex < allData.length) {
// 次のバッチを設定
props.setProperty('batchIndex', endIndex.toString());
console.log('次のバッチを1分後に実行します');
ScriptApp.newTrigger('batchProcess')
.timeBased()
.after(60 * 1000)
.create();
} else {
// 完了
props.deleteProperty('batchIndex');
console.log('全件処理完了!');
// 一時トリガーを削除
deleteOldTriggers('batchProcess');
}
}
function deleteOldTriggers(functionName) {
ScriptApp.getProjectTriggers().forEach(trigger => {
if (trigger.getHandlerFunction() === functionName) {
ScriptApp.deleteTrigger(trigger);
}
});
}
デバッグ用コードテンプレート集
テンプレート1: 処理時間計測
function measureExecutionTime() {
const startTime = Date.now();
// === 計測したい処理 ===
doSomething();
// ======================
const endTime = Date.now();
const duration = (endTime - startTime) / 1000;
console.log(`実行時間: ${duration}秒`);
}
テンプレート2: 進捗表示
function showProgress() {
const total = 1000;
for (let i = 0; i < total; i++) {
// 処理
// 10%ごとに進捗表示
if ((i + 1) % (total / 10) === 0) {
const progress = Math.round(((i + 1) / total) * 100);
console.log(`進捗: ${progress}% (${i + 1}/${total})`);
}
}
}
テンプレート3: デバッグモード切り替え
const DEBUG_MODE = true; // 本番時は false に変更
function debugLog(message, data = null) {
if (!DEBUG_MODE) return;
if (data !== null) {
console.log(`[DEBUG] ${message}:`, JSON.stringify(data, null, 2));
} else {
console.log(`[DEBUG] ${message}`);
}
}
function myFunction() {
debugLog('処理開始');
const data = fetchData();
debugLog('取得データ', data);
const result = processData(data);
debugLog('処理結果', result);
}
テンプレート4: 変数ダンプ
/**
* 変数の中身を詳しく出力
*/
function dump(label, value) {
console.log(`===== ${label} =====`);
console.log('型:', typeof value);
console.log('値:', JSON.stringify(value, null, 2));
if (Array.isArray(value)) {
console.log('配列の長さ:', value.length);
} else if (typeof value === 'object' && value !== null) {
console.log('キー:', Object.keys(value));
}
console.log('='.repeat(label.length + 12));
}
// 使用例
function testDump() {
const user = { name: '山田', age: 30 };
dump('ユーザー情報', user);
const items = ['A', 'B', 'C'];
dump('アイテム配列', items);
}
まとめ
GASデバッグの基本から応用まで解説しました。
デバッグの3原則
- ログを出力する –
console.log()で変数の中身を確認 - エラーをキャッチする –
try-catchで例外を処理 - 小さく分割する – 大きな処理は段階的にテスト
覚えておくべきテクニック
| 状況 | テクニック |
|---|---|
| 変数の確認 | console.log(), JSON.stringify() |
| ステップ実行 | ブレークポイント + デバッガー |
| 例外処理 | try-catch-finally |
| 大量処理 | バッチ分割 + Utilities.sleep() |
| 本番運用 | エラー通知メール |
次のステップ
- GASエラー解決ガイド|TOP10 で具体的なエラーを確認
- GAS×ChatGPT入門 でAI連携を学ぶ
- GAS公式ドキュメント で詳細を確認
エラーは成長のチャンス。この記事で学んだテクニックで、自力解決スキルを身につけましょう!
最終更新: 2026-02-02
コメント