SfSkills Domains Docs GitHub

Home / apex / salesforce-debug-log-analysis

salesforce-debug-log-analysis

Use when the user has captured Salesforce debug logs and needs forensic analysis of runtime behavior — trigger cascades, flow interviews, LWC/Aura @AuraEnabled calls, batch/queueable/future/scheduled Apex, platform events, CDC, validation/workflow/approvals, callouts, DML failures, governor limit hits, managed-package traces, flip-flop fields, UNABLE_TO_LOCK_ROW, INSUFFICIENT_ACCESS, mixed DML.

Open the skill folder on GitHub


Salesforce Debug Log Analysis

Salesforce debug logs look like text but behave like event traces from a distributed system. A single record update can produce tens of thousands of lines across dozens of triggers, flows, LWC controllers, rollup calculations, validation rules, workflow field updates, platform events, and managed packages. The real cause is often in a different transaction than the one the user uploaded, or inside a package whose source you cannot read. This skill captures the complete investigation pattern for every type of Salesforce runtime behavior.

For any Salesforce log, follow this loop in order. Do not skip steps.

  1. Triage: classify every uploaded log by size, start time, and shape before reading content.
  2. Timeline: build a chronological view across all logs with inter-log deltas.
  3. Classify the question: is the user asking about a flip-flop field, a failed DML, a slow transaction, a flow error, an async job, a merge, a permission issue, or something else? Each has a different investigation recipe.
  4. Load the right reference: the references/ folder has dedicated guides for each major category. Load only what applies.
  5. Execute the recipe: run the specific greps and Python extractions for that category.
  6. Report with structure: headline, evidence, mechanism, limits, recommendations.

Step 1: triage every log

Before reading any content, run:

cd /mnt/user-data/uploads
ls -la *.log

for f in *.log; do
  start=$(head -3 "$f" | tail -1 | awk '{print $1}')
  size=$(wc -c < "$f")
  entry=$(head -5 "$f" | grep -oE "EXECUTION_STARTED|BATCH_APEX|CRON_|CODE_UNIT_STARTED[^|]*\|[^|]*" | head -1)
  echo "$start  size=$size  $f  entry=$entry"
done | sort

Classify each log by shape:

ShapeSizeSignalMeaning
Parent synchronousunder 1MBOpens with a top-level CODE_UNIT_STARTED, shallow nestingUI action, API call, or controller entry that started something
Cascade (trigger/flow storm)2 to 10MB+Many CODE_UNIT_STARTED.*trigger, FLOW_START_INTERVIEW_BEGINDownstream firestorm caused by a DML
Batch executionvariableBATCH_APEX_START, BATCH_ID=Batch Apex execute() scope
Queueablemedium`CODE_UNIT_STARTED[EventService....queueable`Async job
Future methodmedium`CODE_UNIT_STARTED[future]or namespace +System.future`@future continuation
ScheduledmediumCRON_TRIGGER_ in headerScheduled Apex fire
Platform event triggermediumTrigger on *__e ObjectEvent subscriber firing
Change Data CapturemediumTrigger on *__ChangeEventCDC subscriber firing
LWC/Aura controller callsmall to medium`CODE_UNIT_STARTED[EventService.....auraor.apex]Controller.`@AuraEnabled method invoked
Visualforcesmall to mediumVF_PAGE_MESSAGE, VF_APEX_CALLVF controller lifecycle
REST/SOAP servicemediumEXECUTION_STARTED then Apex REST resource classExternal API hitting custom Apex endpoint
Test executionmediumCODE_UNIT_STARTED.*test, TESTING_LIMITS@isTest run

Step 2: build the timeline

python3 << 'EOF'
import re, os
logs = []
for f in sorted(os.listdir('.')):
    if f.endswith('.log'):
        with open(f) as fp:
            fp.readline()
            m = re.match(r'(\d{2}:\d{2}:\d{2}\.\d+)', fp.readline())
            if m: logs.append((m.group(1), f, os.path.getsize(f)))
logs.sort()
prev = None
for t, f, s in logs:
    delta = ""
    if prev:
        def sec(x):
            h,m,s = x.split(':'); return int(h)*3600+int(m)*60+float(s)
        delta = f"+{sec(t)-sec(prev):.1f}s"
    tp = "small" if s < 1_000_000 else "large"
    print(f"{t}  {tp:5s}  {s:>10}  {f}  {delta}")
    prev = t
EOF

Patterns in deltas:

Step 3: classify the question

Based on what the user is asking, match to the primary investigation category below. The reference file for each category is named in the right column.

User's symptom or questionCategoryLoad reference
"Why is this field changing?" "Flip-flop"Field write attributionflows.md, apex-and-async.md, managed-packages.md, recipes.md
"Why did my flow fail?" "Flow error"Flow diagnosticsflows.md, error-codes.md
"My LWC is slow/erroring" "Aura action failed"UI framework Apex callsui-frameworks.md
"My batch is stuck/failing"Async Apexapex-and-async.md, recipes.md
"Platform event not firing" "CDC trigger not running"Event-driven Apexapex-and-async.md, integration.md
"UNABLE_TO_LOCK_ROW" "Deadlock"Concurrencyerror-codes.md, recipes.md
"INSUFFICIENT_ACCESS" "merge won't work" "can't see record"Sharing and FLSerror-codes.md, security-sharing.md, recipes.md
"Too many SOQL" "CPU timeout" "Heap exceeded"Governor limitsgovernor-and-performance.md, recipes.md
"Workflow rule not firing" "Process Builder" "Approval"Legacy automationlegacy-automation.md
"Validation rule blocking"Validationerror-codes.md
"Callout failing" "Named credential" "External service"Integrationintegration.md, recipes.md
"What's this xyz namespace?"Unknown managed packagemanaged-packages.md
"My test is failing but works in prod"Test executionapex-and-async.md
"VF page throwing"Visualforceui-frameworks.md
"Duplicate rule blocked" "Matching rule"Duplicate handlingspecialized-topics.md
"Omni-Channel routing issue" "Skill-based routing"Service Cloud routingspecialized-topics.md
"Einstein bot error" "NBA strategy failed"AI featuresspecialized-topics.md
"Continuation timeout" "Transaction Finalizer"Advanced asyncspecialized-topics.md
"Big Object query" "Custom Metadata query"Specialized data storesspecialized-topics.md
"Lead conversion issue" "Case merge failure"Lifecycle operationsspecialized-topics.md, recipes.md
"Community user can't see" "Guest User"Experience Cloudsecurity-sharing.md, ui-frameworks.md
"Encrypted field masked" "Shield"Encryptionsecurity-sharing.md, specialized-topics.md
"User needs to see X but can't"Access modelsecurity-sharing.md, recipes.md
"Slack/Quip/Heroku Connect"External platform integrationspecialized-topics.md, integration.md

If the question touches multiple categories (common), load multiple references.

Step 4: universal patterns worth knowing before opening any reference

Track field value transitions (flip-flop diagnostics)

import re
for f in sorted_logs:
    with open(f) as fp: content = fp.read()
    matches = re.findall(r'RECORD_ID_FRAGMENT[^}]*?FIELD_NAME[=:]"?([^,"}]+)"?', content)
    prev = None
    for i, m in enumerate(matches):
        if m != prev:
            print(f"{f} [{i:3d}] {m}")
            prev = m

Only print transitions. The transition index reveals how deep in the cascade the flip happens. Consistent index across logs = same automation responsible.

Extract the full execution cascade

# Every code unit and flow interview that fired, in order
grep -E "CODE_UNIT_STARTED|FLOW_START_INTERVIEW_BEGIN|BATCH_APEX|VF_APEX_CALL" log.log | head -60

Extract all DML operations

grep -E "DML_BEGIN|DML_END" log.log | head -50

Extract all exceptions

grep -E "EXCEPTION_THROWN|FATAL_ERROR|FLOW_ELEMENT_FAULT|VALIDATION_FAIL" log.log

Identify the running user and context

Every log has a header with the user ID, organization, and trace flag levels. Check:

head -20 log.log | grep -E "USER_INFO|EXECUTION_STARTED|APEX_CODE"

Also check LastModifiedById in any FLOW_VALUE_ASSIGNMENT record dumps. If it points to an integration user (0050B..., 005...), you are looking at automation context. If it points to a human user (005...), it's a UI or API action by that person.

Step 5: identify who wrote a field (the four mechanisms)

A field can change through exactly four mechanisms. Each has a different log signature. Before accusing any specific automation, classify which mechanism is in play:

  1. Direct Apex assignment: VARIABLE_ASSIGNMENT target inside a Trigger or Apex class CODE_UNIT_STARTED block. Grep: grep -B2 "FIELD_NAME =" log.log
  2. Flow assignment: FLOW_ASSIGNMENT_DETAIL|<flow-id>|<variable>|<value> or the field in FLOW_VALUE_ASSIGNMENT as a changed value. Grep: grep "FLOW_ASSIGNMENT_DETAIL" log.log | grep FIELD_NAME
  3. Rollup recalculation (DLRS, native roll-up summary, Apex rollup): field appears in before/after record snapshots with a different value, but nothing targets it in any assignment event. Before-update triggers or master-detail rollups recalculated it.
  4. Formula or calculated field: never actually stored. Recalculated on every read. If grep finds it in SELECT queries but never as an assignment target, it is a formula. You cannot trace who "changed" it because nothing writes to it.

Before blaming a flow or trigger, determine which mechanism applies. Check the field metadata if you can: rollups have SummaryField__c markers, formulas have Formula__c markers in record dumps.

Step 6: report with structure

When reporting to the user, always use this structure:

  1. Headline: one sentence naming the root cause.
  2. Evidence: specific log events (with timestamps or line numbers) that support it.
  3. Timeline: if multi-log, the ordered timeline with deltas.
  4. Mechanism: how this causes the symptom, in terms of SF platform behavior.
  5. What the log cannot tell you: explicit limits on what the log shows.
  6. Recommendations: ordered from "stop the bleeding" to "long-term fix".

Do not dump raw log content. Summarize and quote only specific lines that matter.

Limits: be explicit about what the log cannot tell you

The log cannot tell you:

When hitting one of these, state so plainly. Give the user the concrete next step outside the log: impersonate user X, query child records as admin vs integration user, open the field setup page, check Setup > Monitoring > Apex Jobs, etc.

Reference files

The references/ folder has dedicated guides. Load the ones relevant to the category you identified in step 3:

Read these only when they apply. Do not preload all of them.

Official Sources Used

Package files