5.7.2 Execution Log Analysis

An execution log is the evidence to collect when “Bazel rebuilt it” is only the symptom. It records the spawns that participated in an invocation, including their arguments, environment, inputs, outputs, execution metadata, and cache information. Comparing two logs lets you move from a rebuild cascade to the first action whose recorded state actually changed.1

This is different from 5.2.3 bazel aquery — Action Graph: aquery describes actions created by analysis, while an execution log describes spawn-level execution evidence from one invocation. It is also different from the experimental execution graph log in 5.4.6 Execution Log Graph.

The shared-policy controls in 3.2.6 Hermeticity Settings reduce common host leaks. Paired logs are the next step when a divergence remains after those controls are in place.

Capture a comparable pair

Use the compact format for new investigations:

bazel build //app:release \
  --execution_log_compact_file=/tmp/exec-before.log

bazel build //app:release \
  --execution_log_compact_file=/tmp/exec-after.log

The stable flag is --execution_log_compact_file. Bazel 9 documents the file as a Zstandard-compressed stream of length-delimited ExecLogEntry protobuf messages. The older binary and newline-delimited JSON formats remain available through --execution_log_binary_file and --execution_log_json_file, but Bazel recommends compact because it is significantly smaller and cheaper to produce. The three output flags are mutually exclusive.2

Do not attach a universal size ratio to that recommendation. Log size depends on the action graph and its input structure. Compact encoding retains structural back-references instead of eagerly repeating large input sets, so the saving can be dramatic, but a measurement from the Bazel repository is an example rather than a capacity-planning constant.3

The compact format appeared experimentally during the Bazel 7 line and is documented as stable in Bazel 8 and later. Older 7.x material may therefore show --experimental_execution_log_compact_file. Check bazel help build for the binary that will produce the log instead of copying that spelling into a shared .bazelrc.3 Bazel 7.2 added automatic upload of an enabled compact log to a configured Build Event Service. Collection is still not enabled by default, and retrieval or analysis depends on what the BES backend exposes.4

A useful pair changes only the factor under investigation. Keep the target, revision, Bazel version, configuration, platform, toolchain, and relevant host environment recorded. First compare repeated builds on one machine. Compare CI with a developer machine only after that baseline behaves as expected. Otherwise the diff mixes the suspected cause with unrelated configuration drift.1

Read the first divergence, not the largest cascade

The most visible difference is often downstream damage. A changed generated header can invalidate hundreds of consumers, but those consumers are not hundreds of independent root causes. Start at the earliest meaningful difference and classify it:

Difference in the producing spawnWhat it meansNext question
Input path or digestThe declared input state changedWas the edit expected, or did an unstable generated file enter the graph?
Argument or environment valueThe action identity changedWhich rc setting, rule attribute, toolchain, or host value supplied it?
Platform or execution propertyThe execution configuration changedAre the two invocations actually in the same comparison cohort?
Output digest, with no recorded input differenceEvidence of possible non-hermeticityCan two independent cache-bypassing runs reproduce the difference?

The last row is a lead, not an automatic verdict. The log can show that recorded inputs agree while output bytes differ. It cannot prove that every real input was declared. That mismatch is exactly what makes an action non-hermetic. Trace the first changed output to its producer, then inspect undeclared clocks, randomness, filesystem state, network access, locale, and unstable ordering as candidates. 2.3.3 Non-Determinism Sources explains how those sources propagate through the build.

Likewise, “the action ran again” does not by itself prove cache poisoning. A changed command, environment, input, platform, or cache-read policy explains an ordinary miss. 5.5.3 Remote Cache Diagnostics provides the stronger controls needed to prove that the same action identity reused a wrong cached result.

Choose a comparison tool

Bazel's source tree contains //src/tools/execlog:parser, which accepts a pair of logs, converts them to text, and aligns actions for an ordinary text diff. It also contains //src/tools/execlog:converter for converting among supported formats. These are source-tree targets, not commands installed beside every Bazel binary, so build them from a matching Bazel checkout.1

For compact logs, bb explain performs a structural comparison directly. It can surface changed inputs, arguments, environment values, and outputs, then group their transitive effects so the initial cause is not buried under downstream re-execution. It can compare local log files without a BuildBuddy account. BuildBuddy-backed workflows can also fetch logs by invocation identity. Treat this as an external BuildBuddy CLI capability, not as a Bazel subcommand.3

bb explain /tmp/exec-before.log /tmp/exec-after.log

When the comparison isolates two different output artifacts, use a byte- or format-aware tool for the second question: how do the artifacts differ? Diffoscope is useful for archives and binaries because it can expose embedded timestamps, paths, ordering, or metadata. It does not explain why Bazel chose to execute an action, and it does not read execution logs. Use it after the log comparison has identified the relevant producer and outputs.3

Finally, do not expect --execution_log_sort to normalize compact logs. That flag sorts only binary and JSON logs. Compact logs are never sorted. Use a structure-aware comparator rather than a raw byte diff of two compact files.2

key takeaway

Capture two controlled invocations with --execution_log_compact_file, keeping the workload and execution context matched. Compare spawn structure to locate the first changed input, argument, environment value, platform property, or output instead of treating every downstream rerun as a root cause.

Use Bazel's source-tree parser for aligned textual comparison or bb explain for a structural compact-log diff. An output change without a recorded input change is evidence to investigate non-hermeticity, not proof by itself. Confirm it with independent runs and inspect the isolated artifacts with a tool such as Diffoscope.

Check your understanding · 3 questions

1.A small source edit re-executed hundreds of actions. What should you inspect first in paired execution logs?

Select one answer

2.Match each tool or flag to its role:

Drag each answer onto the matching prompt, or click an answer and then click a prompt

Answers
--execution_log_compact_file
bb explain
Diffoscope

3.Decide which conclusions are supported by execution-log evidence.

Choose True or False for each sentence

Different environment values can explain why an action missed the cache.
A changed output with unchanged recorded inputs proves every real input was declared.
Raw byte-diffing compact logs is preferable because compact logs are sorted.
A controlled comparison keeps target, configuration, platform, and toolchain matched.
0 of 3 answered

Footnotes

  1. Debugging Remote Cache Hits for Remote Execution — controlled same-machine and cross-machine comparisons, execution-log contents, and Bazel's parser and converter workflow 1 2 3

  2. Bazel Command-Line Reference — current execution-log flags, protobuf encodings, Zstandard compression, mutual exclusion, and sorting scope (verified with Bazel 9.0.0) 1 2

  3. bb explain: Understanding Why Bazel Rebuilds — compact-log structure, local-file and hosted comparison workflows, transitive root-cause analysis, non-hermetic output detection, and Diffoscope handoff 1 2 3 4

  4. What's New in Bazel 7.2 — compact-log overhead, opt-in collection, and Bazel 7.2 upload to a configured Build Event Service