sf-debug について
Salesforce 開発者向けのデバッグとトラブルシューティングガイドです。ログ、例外、性能上のボトルネックを扱い、原因調査の指針に加えて、ガバナ制限、CLI によるログ操作、プロファイリングを解説します。
主な機能
- 01Salesforce のデバッグログと実行フローの分析
- 02ガバナ制限の使用状況の確認
- 03一般的な例外の診断と原因の追跡
- 04sf CLI によるログの取得とリアルタイム表示
- 05チェックポイントとクエリプランを使った調査
- 06CPU、ヒープ、コード性能のプロファイリング
活用シーン
- 01Apex の例外やトリガー失敗の調査
- 02SOQL または DML の制限超過の特定
- 03Salesforce 組織のログのリアルタイム確認
- 04クエリループや高 CPU 使用などの性能問題の特定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ clientell-ai/ salesforce-skills --skill sf-debugインストール後、Claude Code で/sf-debugを入力して読み込まれたか確認します。
同梱ファイルreferences/debug-reference.md
詳細
Salesforce デバッグ・トラブルシューティングの専門家
Salesforce のデバッグを専門としています。デバッグログ、ガバナ制限違反、例外、パフォーマンスのボトルネックから問題を診断します。根本原因の分析と、実行可能な修正方法を提示します。
1. デバッグログの分析
ログレベル(詳細度の高い順)
| レベル | 用途 |
|---|---|
| FINEST | 完全なトレース — 変数の値、フレームワーク内部の呼び出し |
| FINER | 詳細なフロー — パラメーターを含むメソッドの開始・終了 |
| FINE | 主な判断ポイントとループの反復 |
| DEBUG | 一般的な診断情報 |
| INFO | トランザクションの主な節目 |
| WARN | 問題を示唆する可能性がある、回復可能な問題 |
| ERROR | 直ちに対処が必要な失敗 |
ログカテゴリ
| カテゴリ | 記録される内容 |
|---|---|
Apex_code | Apex の実行、System.debug() の出力、変数の割り当て |
Apex_profiling | 累積リソース使用量 — SOQL、DML、CPU、ヒープ |
Database | SOQL クエリ、DML 操作、クエリプラン、行数 |
System | システムメソッド、プラットフォームイベント、数式の評価 |
Validation | 入力規則、ワークフローの項目更新 |
Workflow | ワークフロー規則、プロセスビルダー、フローの実行 |
Callout | HTTP コールアウト、SOAP 呼び出し、外部サービスからの応答 |
Visualforce | VF ページのレンダリング、ビューステート、コントローラーのアクション |
NBA | Next Best Action 戦略の実行 |
デバッグログの読み方 — 主な行のプレフィックス
EXECUTION_STARTED / EXECUTION_FINISHED — transaction boundaries
CODE_UNIT_STARTED / CODE_UNIT_FINISHED — trigger, class, or method execution
SOQL_EXECUTE_BEGIN / SOQL_EXECUTE_END — query with row count
DML_BEGIN / DML_END — DML operation with row count
EXCEPTION_THROWN — exception type and message
FATAL_ERROR — unrecoverable error with stack trace
HEAP_ALLOCATE — heap memory allocation
LIMIT_USAGE_FOR_NS — governor limit summary per namespace
CUMULATIVE_LIMIT_USAGE — end-of-transaction limit summary
USER_DEBUG — System.debug() output
VARIABLE_SCOPE_BEGIN / VARIABLE_ASSIGNMENT — variable tracking (FINEST)
METHOD_ENTRY / METHOD_EXIT — method call tracking (FINER+)
FLOW_START_INTERVIEWS — flow/process builder execution
VALIDATION_RULE — validation rule evaluation
CALLOUT_REQUEST / CALLOUT_RESPONSE — external HTTP calls
ログの構成
デバッグログは次の順序で記録されます。
EXECUTION_STARTED— トランザクション開始CODE_UNIT_STARTED— トリガーまたはエントリーポイントの実行- トリガー前のロジック(入力規則、項目更新)
- DML の実行とトリガー後のロジック
- ワークフロー規則、プロセスビルダー、フロー
- ワークフローによって項目が更新された場合、トリガー前後の処理を再評価
- コミットまたはロールバック
CUMULATIVE_LIMIT_USAGE— 最終的なガバナ制限の概要EXECUTION_FINISHED— トランザクション終了
2. ガバナ制限の監視
Limits クラスのメソッド — 制限に達する前に確認
// SOQL
System.debug('SOQL queries: ' + Limits.getQueries() + ' / ' + Limits.getLimitQueries());
// DML
System.debug('DML statements: ' + Limits.getDmlStatements() + ' / ' + Limits.getLimitDmlStatements());
System.debug('DML rows: ' + Limits.getDmlRows() + ' / ' + Limits.getLimitDmlRows());
// CPU
System.debug('CPU time (ms): ' + Limits.getCpuTime() + ' / ' + Limits.getLimitCpuTime());
// Heap
System.debug('Heap size (bytes): ' + Limits.getHeapSize() + ' / ' + Limits.getLimitHeapSize());
// Query rows
System.debug('Query rows: ' + Limits.getQueryRows() + ' / ' + Limits.getLimitQueryRows());
// Callouts
System.debug('Callouts: ' + Limits.getCallouts() + ' / ' + Limits.getLimitCallouts());
// Future calls
System.debug('Future calls: ' + Limits.getFutureCalls() + ' / ' + Limits.getLimitFutureCalls());
// Queueable jobs
System.debug('Queueable jobs: ' + Limits.getQueueableJobs() + ' / ' + Limits.getLimitQueueableJobs());
制限を確認するタイミング
- 負荷の高い操作の前 — すぐにリファクタリングできないループ内のクエリまたは DML
- バッチ処理後 —
Database.Batchable.execute()の各バッチの最後 - ユーティリティ/サービスクラス内 — プロファイリングのため、開始時と終了時に制限をログに記録
- catch ブロック内 — LimitException が近づいている可能性がある場合
- 短時間で多数回繰り返すループ内では使用しない —
Limits.*()の呼び出し自体が CPU を消費します
同期処理と非同期処理の制限
| リソース | 同期処理 | 非同期処理(Batch/Future/Queueable) |
|---|---|---|
| SOQL クエリ | 100 | 200 |
| DML ステートメント | 150 | 150 |
| CPU 時間 | 10,000 ms | 60,000 ms |
| ヒープサイズ | 6 MB | 12 MB |
| クエリ行数 | 50,000 | 50,000 |
| コールアウト | 100 | 100 |
| DML 行数 | 10,000 | 10,000 |
3. よくあるエラーの診断
| エラー | 考えられる原因 | 対処方法 |
|---|---|---|
UNABLE_TO_LOCK_ROW | 同じレコード、または主従関係にある親レコードへの同時更新 | FOR UPDATE を指定して再試行する、バッチの対象範囲を小さくする、非同期処理を使う、不要な親レコードの更新を避ける |
ENTITY_IS_DELETED | 同じトランザクション内、または別のユーザーによってすでに削除されたレコードに対する DML | DML の前に isDeleted を確認する、try/catch で同時実行を処理する、トリガーの実行順序を確認する |
FIELD_CUSTOM_VALIDATION_EXCEPTION | 入力規則の失敗 | オブジェクトの入力規則を確認する、項目値がすべての条件を満たしていることを確認する、一部の成功を許容するには Database.insert(records, false) を使う |
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY | 関連レコード(参照関係/主従関係の親、所有者、キュー)へのアクセス権がない | 共有ルールを確認する、OWD を確認する、実行ユーザーが関連レコードにアクセスできることを確認する、明確な理由がある場合にのみ without sharing を使う |
MIXED_DML_OPERATION | 同じトランザクション内で設定オブジェクト(User、Group)と非設定オブジェクトに対する DML を実行している | いずれかの DML を @future に移す、テストでは System.runAs() を使う、別々のトランザクションに分ける |
System.LimitException: Too many SOQL queries | 同期トランザクション内で SOQL クエリが 100 件を超えている | ループの外にクエリを移す、検索にはコレクションと Map を使う、大規模なデータセットには SOQL for ループを使う |
System.LimitException: Too many DML statements | トランザクション内で DML ステートメントが 150 件を超えている | レコードを List にまとめ、ループの外で一括 DML を実行する |
System.CalloutException | HTTP コールアウトの失敗 — タイムアウト、無効なエンドポイント、証明書の問題 | Named Credential の設定を確認する、エンドポイント URL を確認する、再試行でタイムアウトを処理する、リモートサイト設定を確認する |
System.NullPointerException | null 参照に対するメソッド/プロパティへのアクセス | アクセス前に null チェックを追加する、セーフナビゲーション演算子 ?. を使う、アクセス前に SOQL の結果が返されていることを確認する |
System.QueryException: List has no rows | [SELECT ... LIMIT 1] の結果がなく、単一の sObject 変数に割り当てられている | List<SObject> を使って .isEmpty() を確認するか、try/catch で囲む |
System.QueryException: List has more than 1 row | 単一の変数に割り当てたクエリが複数の行を返した | LIMIT 1 を追加するか、List<SObject> を使う。データを調査する — 重複はデータ品質の問題を示している可能性がある |
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY | トリガーの再帰、または連鎖するトリガーの失敗 | 静的な再帰ガードを実装する、トリガーハンドラーフレームワークに再入防止機能があるか確認する |
System.AsyncException | 非同期ジョブのキューへの追加数が多すぎる、またはチェーンの上限に達した | Limits.getQueueableJobs() を確認する、バッチのチェーンには Finalizer を使う、Queueable ごとのキュー追加を 1 件に制限する |
System.SerializationException | Queueable または Platform Event 内にシリアライズできないオブジェクトがある | transient 参照を削除する、シリアライズされた状態でリレーション項目を持つ SObject 型を避ける |
STRING_TOO_LONG | 項目値が最大長を超えている | DML の前に .abbreviate(maxLength) で検証または切り詰めを行う |
エラー診断のワークフロー
- エラーメッセージ全体を読む — Salesforce のエラーは
STATUS_CODE: message形式です - 発生元の行を見つける — スタックトレースで
Class.MethodName: line X, column Yを探します - トリガーのコンテキストを特定する — insert/update の前か後かを確認します。
CODE_UNIT_STARTEDを確認してください - 連鎖する失敗を確認する — 1 つのトリガーの失敗が、親トリガーで
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITYを引き起こす場合があります - 最小限のデータで再現する — Execute Anonymous または対象を絞ったテストメソッドを使います
4. デバッグログの CLI コマンド
ログをリアルタイムで表示する
# Stream logs as they are generated (colored output)
sf apex tail log --target-org myOrg --color
# Tail with specific log level
sf apex tail log --target-org myOrg --debug-level MyDebugLevel
ログを一覧表示して取得する
# List recent debug logs
sf apex log list --target-org myOrg --json
# Get a specific log by ID
sf apex log get --log-id 07Lxxxxxxxxxxxxxxx --target-org myOrg
# Get the most recent log
sf apex log get --number 1 --target-org myOrg
# Get logs and save to file for analysis
sf apex log get --log-id 07Lxxxxxxxxxxxxxxx --target-org myOrg > debug.log
デバッグ出力付きで Apex を実行する
# Execute anonymous Apex and capture output
sf apex run --target-org myOrg --file scripts/debug-script.apex
# Run inline Apex for quick debugging
echo "System.debug(Limits.getQueries());" | sf apex run --target-org myOrg
古いログを削除する
# Clean up old logs to free storage
sf apex log list --target-org myOrg --json | \
sf data delete bulk --sobject ApexLog --file -
5. チェックポイントと Developer Console によるデバッグ
Execute Anonymous によるデバッグ
対象を絞った調査には Execute Anonymous を使います。
// Reproduce an issue with specific data
Account testAcc = [SELECT Id, Name, Industry FROM Account WHERE Id = '001xxxxxxxxxxxx'];
System.debug('Account state: ' + JSON.serializePretty(testAcc));
// Test a specific method in isolation
MyService service = new MyService();
try {
service.processRecord(testAcc);
System.debug('SUCCESS: Method completed without error');
} catch (Exception e) {
System.debug('FAILED: ' + e.getTypeName() + ' - ' + e.getMessage());
System.debug('Stack trace: ' + e.getStackTraceString());
}
// Check governor limits after operation
System.debug('Post-execution SOQL: ' + Limits.getQueries());
System.debug('Post-execution DML: ' + Limits.getDmlStatements());
System.debug('Post-execution CPU: ' + Limits.getCpuTime() + 'ms');
チェックポイント(Developer Console)
- Developer Console で特定の行にチェックポイントを設定します
- チェックポイントは、その実行時点のヒープ状態、ローカル変数、静的変数を取得します
- 同時に有効にできるチェックポイントは最大 5 つです
- チェックポイントは 30 分後に期限切れになります
- 結果は Checkpoint Inspector タブに表示されます
- System.debug() では不十分な場合にチェックポイントを使用します。オブジェクトグラフ全体を取得できます
Developer Console での SOQL クエリのデバッグ
Query Editor → Execute SOQL/SOSL directly
Logs tab → Filter by "DATABASE" events to see query performance
Query Plan tool → Use Tooling API: /services/data/vXX.0/query?explain=SELECT ...
6. パフォーマンスのプロファイリング
CPU ボトルネックの特定
デバッグログで次のパターンを探します。
METHOD_ENTRY/METHOD_EXIT— 対になった項目間の時間を計算します- 操作の規模に対して
CUMULATIVE_LIMIT_USAGEの CPU 時間が長い - ループ内で大量の
HEAP_ALLOCATEが発生している
よくあるパフォーマンス上のアンチパターン
| アンチパターン | ログの兆候 | 修正方法 |
|---|---|---|
| ループ内の SOQL | 同じコード単位内で SOQL_EXECUTE_BEGIN が繰り返される | ループの前にクエリを実行し、検索には Map を使用します |
| ループ内の DML | 同じコード単位内で DML_BEGIN が繰り返される | List にまとめ、ループの後に DML を 1 回実行します |
| 大量のヒープ割り当て | ループ内で大きなバイト数を伴う HEAP_ALLOCATE | SOQL for ループを使用し、バッチで処理します |
| 高コストな describe 呼び出し | Schema.getGlobalDescribe() が繰り返される | 静的変数にキャッシュします |
| ループ内の文字列連結 | ヒープと CPU 時間が増加する | String.join() または List<String> を使用します |
| 絞り込みのない SOQL | SOQL_EXECUTE_END の行数が多い | WHERE フィルターを追加し、選択性の高いインデックス付きフィールドを使用します |
| コレクションのネストされたループ | CPU 使用率が高く、SOQL/DML の兆候がない | Map による検索を使用し、O(n^2) を O(n) に減らします |
CPU 時間のプロファイリングパターン
Long startCpu = Limits.getCpuTime();
// ... operation under test ...
Long endCpu = Limits.getCpuTime();
System.debug('CPU consumed: ' + (endCpu - startCpu) + 'ms for operation X');
ヒープのプロファイリングパターン
Integer heapBefore = Limits.getHeapSize();
// ... operation under test ...
Integer heapAfter = Limits.getHeapSize();
System.debug('Heap delta: ' + (heapAfter - heapBefore) + ' bytes for operation X');
7. トレースフラグ
CLI でのトレースフラグの設定
# Create a debug level first
sf data create record --sobject DebugLevel --target-org myOrg \
--values "DeveloperName='DetailedDebug' MasterLabel='Detailed Debug' \
ApexCode='FINE' ApexProfiling='FINEST' Database='FINE' System='DEBUG' \
Validation='INFO' Workflow='INFO' Callout='INFO' Visualforce='INFO'"
# Query the debug level ID
sf data query --query "SELECT Id FROM DebugLevel WHERE DeveloperName='DetailedDebug'" \
--target-org myOrg --json
# Create a trace flag for a specific user (lasts up to 24 hours)
sf data create record --sobject TraceFlag --target-org myOrg \
--values "TracedEntityId='005xxxxxxxxxxxx' DebugLevelId='7dlxxxxxxxxxxxx' \
LogType='USER_DEBUG' StartDate='2026-03-20T00:00:00.000Z' \
ExpirationDate='2026-03-20T23:59:59.000Z'"
トレースフラグの種類
| LogType | トレース対象 |
|---|---|
USER_DEBUG | 特定のユーザーによるすべてのトランザクション |
CLASS_TRACING | 特定の Apex クラスが関与する実行 |
DEVELOPER_LOG | 現在の Developer Console セッション |
Setup UI でのトレースフラグ
- Setup > Debug Logs > New
- トレース対象のエンティティ(User、Apex Class、Apex Trigger)を選択します
- 開始時刻と終了時刻を設定します(最大 24 時間)
- デバッグレベルを選択します
- 保存します — 期限切れになるか、20 件のログが生成されるまで(いずれか早い方)、ログが取得されます
8. 注意点
デバッグログの切り詰め
- デバッグログは 20 MB で切り詰められます — 大きなトランザクションではログの先頭部分が失われます
- 切り詰められると、ログに
*** Skipped N bytes of detailed logと表示されます - 回避するには、不要なカテゴリのログレベルを下げ、重要でないカテゴリを NONE または ERROR に設定します
- 切り詰められたログにも、末尾に
CUMULATIVE_LIMIT_USAGEが含まれます
ログの保持期間
- デバッグログの保持期間は 24 時間 のみです(またはトレースフラグあたり 20 件のログが蓄積するまで)
- 事後分析用の重要なログは、すぐにダウンロードしてください
- 期限切れになる前に
sf apex log getを使用して、ログをローカルファイルに保存します
トレースフラグの有効期限
- トレースフラグの有効期間は最大 24 時間 です
- 期限切れ後は警告なしでログの取得が停止します
- 再現しにくい問題を再現する前に、トレースフラグを再作成してください
- 組織あたりのデバッグログの上限は 250 MB です(古いログから削除されます)
デバッグによるパフォーマンスへの影響
System.debug()ステートメントは、本番環境でも CPU 時間を消費します- デバッグログへの書き込みにはオーバーヘッドがあり、高いログレベルは実行速度を低下させます
- 複雑なトランザクションでは、ログレベルを FINEST にすると CPU 時間が 2 倍 になる場合があります
- 本番環境へのデプロイ前に、デバッグステートメントを削除するか、実行を制御してください。
// Use a custom setting or custom metadata to gate debug output if (DebugSettings__c.getInstance().EnableDetailedLogging__c) { System.debug(LoggingLevel.FINE, 'Detailed: ' + JSON.serialize(records)); }
本番環境での System.debug()
- 実行中のユーザーにトレースフラグが有効でない限り、デバッグステートメントは取得されません
- トレースフラグが設定されているかどうかにかかわらず、CPU 時間は消費されます
- 機密データ(PII、認証情報、トークン)を含む
System.debug()は使用しないでください - 本番環境の可観測性には、カスタムのロギングフレームワーク(Platform Events + Big Objects)の使用を推奨します
その他の注意点
System.debug()は引数に対してtoString()を呼び出します。オブジェクトグラフに null 参照があると、NullPointerException が発生する場合があります- 集計クエリ(
COUNT()、SUM())は、集計結果ごとに 1 クエリ行を消費します Database.setSavepoint()とDatabase.rollback()は DML ステートメントとしてカウントされます- after トリガー内の Trigger.new は読み取り専用です。変更すると実行時エラーが発生します
@isTest(SeeAllData=true)を使用するテストは開発環境では成功しても、データの違いにより CI では失敗する場合があります
9. デバッグのワークフロー
ステップごとの手順
-
問題を再現する
- 失敗するユーザー操作、API 呼び出し、自動プロセスを正確に特定します
- 発生した時間帯と、問題を経験しているユーザーを記録します
-
トレースフラグを設定する
# Ensure trace flag is active for the user sf apex tail log --target-org myOrg --color -
問題を発生させてログを取得する
- UI、API、または Execute Anonymous を使用して再現します
- すぐにログを保存します:
sf apex log get --number 1 --target-org myOrg > issue.log
-
まずエラーを確認する
- ログ内で
EXCEPTION_THROWN、FATAL_ERROR、LIMIT_USAGEを検索します - ログが途中で切れている場合は、末尾の
CUMULATIVE_LIMIT_USAGEに注目します
- ログ内で
-
実行パスを追跡する
CODE_UNIT_STARTEDを見つけて、実行されたトリガー/クラスを特定します- 実行順序を追跡します:before トリガー、DML、after トリガー、ワークフロー、プロセスビルダー、フロー
-
ガバナ制限を確認する
LIMIT_USAGE_FOR_NSを確認します。いずれかの制限が 70% を超えていますか?- SOQL の実行回数と
SOQL_EXECUTE_BEGINイベントの数を照合します
-
根本原因を特定する
- データの問題ですか?(レコードの欠落、null フィールド)
- ロジックの問題ですか?(条件の誤り、一括処理対応の不足)
- 制限の問題ですか?(ループ内の SOQL、ループ内の DML)
- 同時実行の問題ですか?(レコードロック、競合状態)
- 設定の問題ですか?(入力規則、共有ルール、権限)
-
修正して検証する
- 正しい修正のうち、最小限のものを適用します
- トレースフラグを有効にしたまま再実行し、問題が解決したことを確認します
- ガバナ制限が改善したことを確認します(単にエラーが消えただけでないこと)
診断用のクイックコマンド
# Search for errors in a saved log
grep -E "EXCEPTION_THROWN|FATAL_ERROR|LIMIT_USAGE" debug.log
# Count SOQL queries in log (look for loops)
grep -c "SOQL_EXECUTE_BEGIN" debug.log
# Count DML operations in log
grep -c "DML_BEGIN" debug.log
# Find slow queries (queries returning many rows)
grep "SOQL_EXECUTE_END" debug.log | grep -E "Rows:[0-9]{3,}"
10. スキル間の連携
| 必要な作業 | 連携先 | 理由 |
|---|---|---|
| Apex コードの修正 | sf-apex | コード変更の生成とレビュー |
| テストの作成/実行 | sf-testing | テストの実行、カバレッジ、アサーション |
| 修正のデプロイ | sf-deploy | デプロイのオーケストレーション |
| データの調査 | sf-data | 組織データのクエリと確認 |
| セキュリティ監査 | sf-security | CRUD/FLS と共有のレビュー |
参考資料
- デバッグリファレンス -- Limits クラスのメソッド、ログの解析パターン、Execute Anonymous のパターン、エラー処理、パフォーマンスプロファイリング、Tooling API のトレースフラグ
- ガバナ制限 -- トランザクションごとの SOQL、DML、CPU、ヒープの制限
コメント
Markdown 対応