Skip to main content

Top Salesforce Apex Debugging Techniques: Logs, Breakpoints, & Best Practices

In plain words: Debugging Apex is like inspecting the black box of an airplane. Instead of guessing why a process failed, you can configure targeted debug logs, step through execution with the Apex Replay Debugger, set memory checkpoints, and filter noise to pinpoint bugs in seconds.

Finding and fixing bugs in Salesforce requires a structured diagnostic approach. Because Apex runs in a multi-tenant cloud environment with strict governor limits, developers cannot simply pause production transactions at will. Mastering platform debugging tools—including Trace Flags, granular logging levels, Anonymous Apex, and interactive replay tools—dramatically reduces troubleshooting time.

1. Configuring Targeted Debug Logs & Trace Flags

Debug logs track database operations, governor limit allocations, workflow rules, and custom Apex execution. Rather than capturing everything and hitting file size caps, configure targeted Trace Flags to collect only what you need.

Step-by-Step: Setting Up a User Trace Flag
  1. Navigate to Setup > Debug Logs in Salesforce.
  2. Click New under the Traced Entity Type section.
  3. Select User (or Automated Process / Apex Class) and select your target user.
  4. Create or select a Debug Level (e.g., set Apex Code to FINEST, Database to INFO, and System to DEBUG).
  5. Set an expiration window and click Save.
360 Debug Log Architecture Card:
  • Max Log Size: Individual logs are capped at 20 MB. Logs exceeding this size are truncated.
  • Storage Retention: Salesforce retains up to 1,000 MB of logs for up to 24 hours before automatic purge.
  • Log Levels: Ordered from least to most verbose: NONE < ERROR < WARN < INFO < DEBUG < FINE < FINER < FINEST.

2. Writing Smart, Structured System.debug Statements

Unstructured debug strings clutter your logs and make searching difficult. Always use explicit LoggingLevel enums and format statements so they can be isolated using log filters.

Real-World Example: Structured Logging with Logging Levels
Differentiating between routine execution tracking, payload inspection, and caught exceptions:
public with sharing class PaymentProcessingService {
    public static void executePayment(Id accountId, Decimal amount) {
        // High-level operational checkpoint
        System.debug(LoggingLevel.INFO, 'Starting payment processing for Account: ' + accountId);

        try {
            if (amount <= 0) {
                System.debug(LoggingLevel.WARN, 'Invalid amount provided: ' + amount);
                throw new IllegalArgumentException('Payment amount must be greater than zero.');
            }

            // Verbose payload debugging for deep troubleshooting
            System.debug(LoggingLevel.FINEST, String.format('Payload check: AccountId={0}, Amount={1}', new Object[]{ accountId, amount }));

            // Payment logic execution...

        } catch (Exception ex) {
            // Critical error logging with full stack trace
            System.debug(LoggingLevel.ERROR, 'Payment Failure: ' + ex.getMessage() + ' | Stack: ' + ex.getStackTraceString());
            throw new AuraHandledException('Payment could not be processed.');
        }
    }
}

3. Rapid Prototyping with Anonymous Apex

The Execute Anonymous window in Developer Console or VS Code allows you to run isolated code blocks on the fly without deploying permanent classes. It is ideal for validating SOQL query syntax, verifying service methods, or inspecting integration payloads.

  • User Context: Anonymous Apex executes with the permissions of the current running user, respecting sharing rules and Object/Field-Level Security.
  • Ephemeral State: Changes persist to the database unless wrapped in a test rollback pattern (Database.rollback()).
  • Open Log Checkbox: Checking Open Log immediately displays the resulting execution tree and debug outputs upon completion.

4. Advanced Debugging: Replay Debugger & Logging Frameworks

Modern enterprise Salesforce development has evolved beyond manual text-log scanning:

  • Apex Replay Debugger (VS Code): Download execution logs directly into VS Code to set breakpoints, inspect variables, and step forward and backward through the code execution timeline locally.
  • Developer Console Checkpoints: Set up to 5 checkpoints to capture in-memory heap dumps and SOQL query statistics at exact execution lines.
  • Open-Source Logging Frameworks (e.g., Nebula Logger): Enterprise orgs deploy persistent logging frameworks that capture exceptions, user transactions, and metadata changes asynchronously into custom objects and platform events.

5. Common Traps & Debugging Best Practices

Developer Trap: Leaving Heavy FINEST Logging Active
Setting debug log filters to FINEST across loops in high-volume triggers causes immediate Log Truncation (hitting the 20 MB cap) and consumes valuable CPU time. Never leave verbose trace flags enabled for long periods in active sandboxes or production.
Core Rule: Use structured LoggingLevel enums in code, filter logs in Developer Console by checking "Debug Only", and use the Apex Replay Debugger in VS Code for zero-cost interactive stepping.
  • Filter by "Debug Only": In Developer Console, check the Debug Only box at the bottom of the log viewer to filter out system noise and view only your explicit System.debug() statements.
  • Clean Up Active Trace Flags: Regularly delete expired or unused trace flags to keep org log generation lean.
  • Always Capture the Stack Trace: When catching exceptions, always log ex.getStackTraceString() alongside ex.getMessage() to locate the exact class and line number of the failure.

Summary

Mastering Apex debugging is about combining real-time log analysis with structured code inspection. By configuring targeted trace flags, writing leveled System.debug statements, leveraging the Apex Replay Debugger in VS Code, and adopting robust logging frameworks, you can troubleshoot complex issues quickly and maintain resilient Salesforce applications.