15.1 Debug Logs & Monitoring
Key Takeaways
- Debug logs capture Apex, SOQL/DML, workflow, validation, callouts, and system events for a traced user, class, or trigger during a request
- Log levels per category run from ERROR through FINEST (plus NONE); higher verbosity increases detail and the chance of hitting the 20 MB log size limit
- Trace flags on users, Apex classes, or triggers control who/what is logged and when the flag expires—without an active trace flag you may see no useful log
- System.debug writes custom messages; combine with execution log limit lines (SOQL, DML, CPU, heap) to diagnose governor and logic failures
- Apex Exception Email notifies designated users of unhandled exceptions; Event Monitoring provides org-wide audit streams (API, login, URI) beyond single-request debug logs
15.1 Debug Logs & Monitoring
Quick Answer: A debug log records what the platform did during a request—Apex, SOQL/DML, validation, workflow, callouts, and more—when a trace flag is active for a user, class, or trigger. Set log levels per category (ERROR → FINEST), stay under the ~20 MB log size limit, use System.debug for custom messages, and read limit usage lines to diagnose governors. Apex Exception Email and Event Monitoring extend monitoring beyond a single log file.
Platform Developer I expects you to debug Apex and automation like a working developer: turn on the right logging, find the failing line, interpret governor limit counters, and know what tools exist when a single debug log is not enough.
What a Debug Log Contains
A debug log is a time-ordered trace of one (or more related) execution(s). Typical entries include:
| Area | What you see |
|---|---|
| CODE_UNIT_STARTED / FINISHED | Controllers, triggers, batch execute, queueable, future, flows invoking Apex |
| SOQL_EXECUTE_BEGIN / END | Query text, rows returned |
| DML_BEGIN / END | Insert/update/delete/undelete operations |
| METHOD_ENTRY / EXIT | Apex method call stack (at fine enough levels) |
| USER_DEBUG | Output from System.debug |
| EXCEPTION_THROWN / FATAL_ERROR | Stack traces and unhandled failures |
| LIMIT_USAGE / CUMULATIVE_LIMIT_USAGE | Governor consumption (SOQL, DML, CPU, heap, callouts, …) |
| VALIDATION_RULE / WORKFLOW / FLOW | Declarative automation that fired in the same transaction |
| CALLOUT_REQUEST / RESPONSE | HTTP callout details when logging is enabled for that category |
Logs are not a substitute for unit tests, but they are the primary runtime microscope when a bug only appears with real data, sharing rules, or multi-automation order of execution.
Log Levels and Categories
Salesforce assigns a log level to each category. Verbosity from least to most detail:
| Level | Typical use |
|---|---|
| NONE | Suppress that category |
| ERROR | Errors only |
| WARN | Warnings and errors |
| INFO | High-level milestones |
| DEBUG | Standard development detail (common default for Apex Code) |
| FINE | Deeper internal detail |
| FINER | Very verbose |
| FINEST | Maximum detail—large logs fast |
Categories you will see on exams and in Setup include (names can vary slightly by UI generation, but the ideas are stable):
- Apex Code — your classes, triggers, anonymous Apex
- Apex Profiling — cumulative limits and profiling lines
- Database — SOQL, SOSL, DML
- Workflow — workflow rules, some process automation detail
- Validation — validation rules
- Callouts — HTTP callouts
- Visualforce — VF page/controller detail
- System — platform/system messages
Exam mindset: Raise Apex Code and Database (and Apex Profiling) when hunting SOQL-in-a-loop or limit exceptions. Raise Workflow / Validation when the bug might be declarative. Avoid FINEST on every category for a busy admin user—you will truncate the log before you reach the failure.
Trace Flags: Who Gets Logged
A trace flag tells Salesforce which user, Apex class, or trigger to log and until when. Key points:
- User-based trace flags log work performed in that user’s context (UI, API, anonymous Apex as that user).
- Class/trigger trace flags focus logging on specific Apex units—useful when many users hit shared code.
- Trace flags have an expiration; expired flags stop producing useful logs.
- Debug logs can be started from Setup → Debug Logs, Developer Console, VS Code / CLI tooling, or related monitoring UIs.
Without an active trace flag covering the executing context, you may run a failing transaction and still have no log—a classic “I reproduced it but there’s nothing in Debug Logs” trap.
Log Size Limits and Truncation
Debug logs have a maximum size commonly tested as 20 MB per log. If the log exceeds that limit, Salesforce truncates it. Truncation often drops the end of the execution—exactly where your exception or final limit dump might have been—so a truncated log can look “fine” until mid-transaction and then stop.
Mitigations developers use:
- Lower noisy categories (Workflow FINEST while debugging pure Apex is wasteful).
- Narrow the trace (specific class flag vs logging an integration user for an hour).
- Reproduce with smaller data volumes or a single record path first.
- Use checkpoints (Developer Console) or targeted
System.debugnear the suspected branch instead of FINEST everywhere. - Prefer unit tests with assertions when the bug is reproducible in Apex tests—logs are for residual runtime mystery.
Orgs also retain only a limited volume/time window of debug logs (commonly discussed as short retention, on the order of a day, with total storage caps). Download important logs promptly; do not assume last week’s failure is still in Setup.
System.debug and Logging Levels in Code
System.debug('Simple message'); // default level
System.debug(LoggingLevel.ERROR, 'Payment failed: ' + orderId);
System.debug(LoggingLevel.DEBUG, 'Rows: ' + accounts.size());
System.debug(LoggingLevel.FINE, 'Full payload: ' + JSON.serialize(payload));
System.debug emits USER_DEBUG lines. Overloads accept a LoggingLevel so messages appear only when the Apex Code category is at least that verbose. Practical habits:
- Log Ids and counts, not entire org-sized lists at FINEST.
- Never log secrets (session IDs, tokens, passwords, full PII) — logs are readable by users with log access.
- Guard expensive serialization in production paths if heap/CPU matter; debug statements still cost CPU and heap.
Reading Execution Logs for Limits and SOQL
When a transaction hits a governor, the log (or the exception email) usually shows a message such as “Too many SOQL queries: 101” along with CUMULATIVE_LIMIT_USAGE style summaries. Train yourself to scan for:
| Signal | Meaning |
|---|---|
| Many SOQL_EXECUTE lines inside a loop pattern | Classic SOQL-in-loop; bulkify with maps |
| High Number of DML statements | DML in a loop; collect and perform one DML |
| CPU time near the limit | Inefficient loops, repeated describe, heavy JSON |
| Heap size pressure | Large lists in memory, big debug strings |
| Row counts vs expectations | Wrong filter, missing sharing, unexpected children |
| Order of CODE_UNIT blocks | Which trigger/class/flow ran before the failure |
Workflow for a limit exception:
- Open the log for the failing user/time.
- Jump to FATAL_ERROR / exception stack.
- Note the class and line.
- Scroll limit usage sections—confirm which governor was exhausted.
- Count SOQL/DML in the hot path; map to bulk patterns (earlier chapters on SOQL/DML and triggers).
- Fix, add a regression test, re-run with a modest log level to confirm.
Anonymous Apex and tests produce logs too—use them to isolate a method without clicking through the entire UI.
Apex Exception Email
Apex Exception Email (configured in Setup) sends notifications when unhandled Apex exceptions occur. Designated recipients (often developers or a distribution list) get the exception type, message, and stack-related detail. This is critical for async failures (batch, queueable, future, scheduled) that no user is staring at in the browser.
Exam-relevant ideas:
- Exception email is not a full debug log substitute, but it points you to the failing class/line.
- Handled exceptions (
try/catchthat swallows without rethrow or logging) may never surface—bad catch blocks hide production bugs. - Combine exception email with targeted debug logs or App/custom logging objects for recurring jobs.
Event Monitoring (High-Level Awareness)
Event Monitoring (part of broader Salesforce Shield / event log capabilities, product packaging depends on edition and licenses) provides org-level event streams such as API calls, logins, URI/page events, report exports, and other security/compliance-oriented telemetry. Contrast with debug logs:
| Concern | Debug logs | Event Monitoring |
|---|---|---|
| Grain | Single transaction detail for traced context | Org-wide event types over time |
| Typical user | Developer fixing a bug | Security, compliance, performance analytics |
| SOQL line-by-line | Yes (when logged) | Not a substitute for Apex line debugging |
| Login/API audit | Limited | Strong fit |
For Platform Developer I, know that Event Monitoring exists, that it is aimed at audit and usage analytics, and that day-to-day Apex debugging still centers on debug logs + tests + exception email. You are not expected to configure every event type in depth, but you should not claim debug logs are the only monitoring surface in Salesforce.
Putting It Together for the Exam
Scenario stems often ask: Why is there no log? (missing/expired trace flag), Why is the log incomplete? (20 MB truncation / too-verbose levels), How do you see SOQL count? (Database + profiling / limit lines), or How are async failures noticed? (Apex Exception Email). Match the tool to the question:
- Trace flag → enable capture for user/class/trigger with expiration.
- Log levels → enough detail without truncation.
- System.debug → intentional breadcrumbs at appropriate LoggingLevel.
- Limit sections → prove which governor and which query/DML pattern failed.
- Exception email / Event Monitoring → production awareness beyond one Developer Console session.
Master reading a log end-to-end and you will clear most debugging items quickly—and debug real orgs faster after you pass.
A developer reproduces a trigger bug as a standard user but finds no new entries under Setup → Debug Logs. What is the most likely cause?
While debugging a bulk update, the debug log stops mid-transaction and never shows the final exception. What should the developer consider first?
Which pairing best matches the monitoring need?