Performance Profiling & Debug Logs
📖 Concept
Performance profiling is how you identify bottlenecks, optimize slow queries, and ensure your Salesforce solution scales. Understanding debug logs, the Developer Console, and profiling tools is essential.
Debug Logs — your primary profiling tool:
Log Categories:
Database — SOQL queries, DML operations
Workflow — Flow/PB/Workflow rule execution
Validation — Validation rule evaluation
Callout — HTTP callout details
Apex_Code — Apex execution, variable values
System — System-level operations
Log Levels:
NONE → ERROR → WARN → INFO → DEBUG → FINE → FINER → FINEST
The Query Plan Tool: Available in the Developer Console (Query Editor → Query Plan), it shows:
- Whether your query is selective
- Which index (if any) will be used
- Estimated number of rows
- Cost comparison of different query plans
Performance Anti-Patterns:
- Non-selective queries on LDV objects — Full table scans
- SOQL in loops — N queries instead of 1
- Trigger recursion — Same trigger firing multiple times
- Formula field chains — Complex formulas on related objects
- Roll-up summary on LDV — Recalculating across millions of records
- Complex sharing rules — Excessive sharing recalculation
Salesforce Optimizer: A free tool that analyzes your org and provides recommendations:
- Unused custom fields, objects, and automations
- Complex business processes
- Data model issues
- Performance bottlenecks
💻 Code Example
1// Performance Profiling Techniques23public class PerformanceProfiling {45 // 1. Custom timer for profiling6 public class Timer {7 private Long startTime;8 private String label;910 public Timer(String label) {11 this.label = label;12 this.startTime = System.currentTimeMillis();13 }1415 public Long stop() {16 Long elapsed = System.currentTimeMillis() - this.startTime;17 System.debug(LoggingLevel.INFO,18 '[PERF] ' + label + ': ' + elapsed + 'ms');19 return elapsed;20 }21 }2223 // 2. Method profiling wrapper24 public static void profileMethod() {25 Timer overallTimer = new Timer('overallProcess');2627 // Profile query28 Timer queryTimer = new Timer('accountQuery');29 List<Account> accounts = [30 SELECT Id, Name, Industry,31 (SELECT Id, LastName FROM Contacts)32 FROM Account33 WHERE Industry = 'Technology'34 LIMIT 100035 ];36 queryTimer.stop();3738 // Profile processing39 Timer processTimer = new Timer('dataProcessing');40 Map<String, Integer> contactCounts = new Map<String, Integer>();41 for (Account acc : accounts) {42 contactCounts.put(acc.Name, acc.Contacts.size());43 }44 processTimer.stop();4546 // Profile DML47 Timer dmlTimer = new Timer('dmlUpdate');48 List<Account> toUpdate = new List<Account>();49 for (Account acc : accounts) {50 acc.Description = 'Contacts: ' + acc.Contacts.size();51 toUpdate.add(acc);52 }53 update toUpdate;54 dmlTimer.stop();5556 // Overall57 Long total = overallTimer.stop();5859 // Log limit consumption60 System.debug(LoggingLevel.INFO, '[PERF] Limits consumed:');61 System.debug(LoggingLevel.INFO,62 ' SOQL: ' + Limits.getQueries() + '/' + Limits.getLimitQueries());63 System.debug(LoggingLevel.INFO,64 ' DML: ' + Limits.getDmlStatements() + '/' + Limits.getLimitDmlStatements());65 System.debug(LoggingLevel.INFO,66 ' CPU: ' + Limits.getCpuTime() + 'ms/' + Limits.getLimitCpuTime() + 'ms');67 System.debug(LoggingLevel.INFO,68 ' Heap: ' + Limits.getHeapSize() + '/' + Limits.getLimitHeapSize());69 }7071 // 3. Query optimization analysis72 public static void analyzeQueryPerformance() {73 // Selective query — uses index74 Timer t1 = new Timer('Selective (Name index)');75 List<Account> r1 = [76 SELECT Id FROM Account WHERE Name = 'Acme' LIMIT 177 ];78 t1.stop();7980 // Non-selective query — full scan81 Timer t2 = new Timer('Non-selective (Description)');82 List<Account> r2 = [83 SELECT Id FROM Account WHERE Description != null LIMIT 100084 ];85 t2.stop();8687 // Optimized with indexed filter + non-indexed filter88 Timer t3 = new Timer('Optimized (indexed + filter)');89 List<Account> r3 = [90 SELECT Id FROM Account91 WHERE CreatedDate = THIS_YEAR // Indexed — narrows first92 AND Description != null // Non-indexed — filters second93 LIMIT 100094 ];95 t3.stop();96 }9798 // 4. Platform Cache for repeat queries99 // Requires Platform Cache allocation in org100 public static List<Account> getCachedAccounts(String key) {101 // Check cache first102 Cache.OrgPartition orgPart = Cache.Org.getPartition('local.CachePartition');103104 List<Account> cached = (List<Account>) orgPart.get(key);105 if (cached != null) {106 System.debug('Cache HIT for: ' + key);107 return cached;108 }109110 System.debug('Cache MISS for: ' + key);111 List<Account> accounts = [112 SELECT Id, Name, Industry FROM Account LIMIT 100113 ];114115 // Store in cache (TTL: 3600 seconds = 1 hour)116 orgPart.put(key, accounts, 3600);117118 return accounts;119 }120121 // 5. Debug log configuration (for team setup)122 // Setup → Debug Logs → New → Select User → Set levels:123 // Database: FINE (see all SOQL)124 // Apex_Code: DEBUG (see execution flow)125 // Workflow: INFO (see automation)126 // System: WARN (reduce noise)127 // Validation: INFO128129 // 6. Analyzing debug log output130 // Look for patterns:131 // SOQL_EXECUTE_BEGIN — each query with bind values132 // SOQL_EXECUTE_END — rows returned, time133 // DML_BEGIN / DML_END — DML operations134 // CODE_UNIT_STARTED / CODE_UNIT_FINISHED — trigger/class entry/exit135 // LIMIT_USAGE_FOR_NS — cumulative limit consumption136}
🏋️ Practice Exercise
Performance Profiling Practice:
- Create a Timer utility class and profile the execution time of different operations
- Use the Developer Console to analyze debug logs for a complex transaction
- Use the Query Plan tool to compare selectivity of 5 different SOQL queries
- Set up Platform Cache and measure the performance improvement for repeated queries
- Profile a trigger with 200 records and identify the bottleneck (SOQL, CPU, or DML)
- Run the Salesforce Optimizer and implement the top 5 recommendations
- Create a performance test that measures operation time with 1, 50, 100, and 200 records
- Analyze a debug log to trace a trigger cascade (Account → Contact → Task)
- Build a custom performance monitoring object that logs transaction metrics
- Write a load test using Data Loader to simulate production-scale data volumes
⚠️ Common Mistakes
Setting debug log levels too high (FINEST for everything) — this slows performance and makes logs unreadable. Use FINE for Database, DEBUG for Apex, WARN for everything else
Profiling in development with small data — performance issues only appear with production-scale data. Always test with realistic volumes
Not using the Query Plan tool — guessing at query performance instead of using the tool that tells you exactly what's happening
Ignoring the 'CUMULATIVE_LIMIT_USAGE' section in debug logs — this section shows total limit consumption across the entire transaction, including all triggers and automations
Optimizing without measuring — always profile first. The actual bottleneck is often not what you expect
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Performance Profiling & Debug Logs. Login to unlock this feature.