LinuxLXXXII · Root Cause AnalysisMethod
Reconstructing the timeline - evidence over recollection
What you'll learn
- Build an incident timeline from timestamped sources rather than from recollection
- Normalise timestamps across hosts, timezones and log formats
- Track the system timeline and the response timeline as separate series
- Treat an unexplained gap in the timeline as a finding rather than as noise
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11
The timeline is the deliverable that everything else in the review is derived from. Get it right and the trigger is obvious, the contributing factors fall out of the gaps, and disagreements about what happened resolve themselves by pointing at a line. Get it wrong and the review produces a story that several people remember differently and nobody can check.
The single most important property of a timeline is that every line has a source. Not “we think the deploy went out around two” - a log line, a commit, a ticket transition, a message with a timestamp.
Memory is not evidence
Human recollection of an incident is systematically distorted in ways that all point the same direction.
- Compression. Forty minutes of confusion is remembered as ten. Ask anybody how long they spent before escalating and the answer is roughly half the real figure.
- Reordering. People remember the order in which they understood things, not the order in which they happened. The hypothesis that turned out to be right migrates earlier.
- Anchoring on the outcome. Once you know the cause, every earlier observation seems to have pointed at it, including the ones that pointed elsewhere at the time.
- Loss of the discarded. The three hypotheses that were tested and rejected vanish from the account entirely - and those are exactly what tell you whether the evidence available was sufficient.
None of this is carelessness; it is how recall works under stress. It is also why the timeline is built from artefacts first, and interviews used only to explain the artefacts.
The sources
Sweep all of them. An incident that touched several layers has evidence in several places, and the interesting lines are usually in the source nobody thought to check.
- The journal on every affected host, at sub-second precision
- Application logs, which are frequently not in the journal and frequently in a different timezone
- Package and configuration history: dpkg.log, dnf history, the /etc git log, configuration management run reports
- Deployment and CI systems: what was released, to where, at what time
- Monitoring and alerting: when each alert fired, when it was acknowledged, when it resolved
- The chat channel and the incident ticket, which timestamp the human decisions
- Load balancer, proxy and firewall logs, which often show the user-visible start before any host does
- Cloud provider or hypervisor event logs: live migrations, host maintenance, storage events
- The status page, which records what was said and when
Normalise the clocks first
Merging timestamped lines from several sources is only valid if the timestamps are comparable. Two failures make them not comparable, and both are common.
$ timedatectl; chronyc tracking | head -6 Local time: Tue 2026-08-11 15:52:03 UTC
Universal time: Tue 2026-08-11 15:52:03 UTC
RTC time: Tue 2026-08-11 15:52:02
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
Reference ID : 0A000201 (ntp1.example.com)
Stratum : 3
Ref time (UTC) : Tue Aug 11 15:47:24 2026
System time : 0.000318215 seconds slow of NTP time
Last offset : -0.000091447 seconds
RMS offset : 0.000412008 secondsIllustrative output
Timezone. The journal displays in the local timezone by default, application logs may use another, and a cloud console often shows a third. Force everything to UTC before you compare:
journalctl --utc --since '2026-08-11 08:55' --until '2026-08-11 09:30' -o short-precise
Skew. A host whose clock was wrong during the incident produces lines that interleave incorrectly with everything else. The journal records this: a clock jump appears as a discontinuity you can search for.
journalctl -b --no-pager | grep -iE 'time jumped|System clock (wrong|time changed)|Clock (change|jump)'
If a host was skewed, either correct its lines by the known offset and say so explicitly in the timeline, or mark that host as providing ordering within itself only. Silently merging skewed lines is worse than leaving them out, because the result looks authoritative.
Two timelines, tracked separately
Keep the system timeline and the response timeline in separate columns. They answer different questions, and merging them hides the most valuable measurement in the review.
System timeline: what the system did. Trigger, first degradation, first user-visible impact, cascading failures, recovery.
Response timeline: what people did. First alert, first human acknowledgement, incident declared, first mitigation attempted, service restored, all-clear.
The intervals between the two are the numbers that matter:
09:14:02 SYSTEM upstream TLS verification begins failing
09:14:30 SYSTEM circuit breaker opens; 100% of API requests fail
09:17:00 SYSTEM first customer-visible errors on the status path
09:31:11 RESPONSE first alert fires (synthetic check, 15 min interval)
09:33:40 RESPONSE on-call acknowledges
09:38:02 RESPONSE incident declared SEV1
09:41:15 RESPONSE hypothesis: expired certificate
09:46:30 RESPONSE new certificate issued and deployed
09:47:52 SYSTEM error rate returns to baseline
time to detect 09:14:02 -> 09:31:11 = 17 m 09 s
time to diagnose 09:33:40 -> 09:41:15 = 7 m 35 s
time to restore 09:41:15 -> 09:47:52 = 6 m 37 s
total duration 09:14:02 -> 09:47:52 = 33 m 50 s
Time to detect is seventeen minutes of a thirty-four minute incident. That single number is usually the largest and cheapest improvement available, and it is invisible in a merged timeline because the alert line sits between two system lines and looks like part of the story rather than a delay in it.
Note also what the decomposition exposes: the synthetic check runs every fifteen minutes, so detection latency was bounded by the check interval rather than by anyone being slow. That is a contributing factor with an obvious action, and it came out of the arithmetic rather than out of anybody’s opinion.
Building it
$ for h in app01 app02 lb01 db01; do ssh "$h" "journalctl --utc --since '2026-08-11 09:00' --until '2026-08-11 10:00' -o short-iso-precise --no-pager"; done | sort -k1,1 > /var/tmp/incident-merged.txt; wc -l /var/tmp/incident-merged.txt4187 /var/tmp/incident-merged.txtIllustrative output
Four thousand lines is not a timeline; it is the raw material. Reduce it by pulling out only the lines that change your understanding, and give each one a source reference so a reader can go back to it.
TIME (UTC) TYPE HOST EVENT SOURCE
09:14:02.771 SYSTEM app01 x509 expired on upstream api journal:myapp
09:14:30.114 SYSTEM app01 circuit breaker opened journal:myapp
09:31:11.000 RESPONSE - synthetic check alert fired alertmanager
09:33:40.000 RESPONSE - on-call acknowledged pagerduty
09:41:15.000 RESPONSE - hypothesis raised in channel slack #inc-482
09:46:30.902 SYSTEM lb01 new certificate loaded, reload ok journal:nginx
09:47:52.418 SYSTEM app01 error rate at baseline prometheus
Seven lines, each with a source. That is a timeline. A reviewer who was not there can check every line, and disagreements are settled by looking rather than by remembering.
Gaps are findings
When the timeline is assembled, look at what is missing. Three patterns are worth a contributing factor each.
A long interval with no lines at all. Nothing was logged and nobody wrote anything down. Usually this is the period when everyone was busy, and it is where the response is least understood. Ask what was happening and record it, even approximately, marked as reconstructed rather than evidenced.
Impact before detection. Any interval between the first user-visible symptom and the first alert is a detection gap with a size attached. It converts directly into an action item.
An observation that does not fit. A line that your explanation does not account for. This is the most valuable thing in the timeline and the most tempting to leave out, because it complicates a story that was nearly finished.
Knowledge check
Knowledge check · 6 questions
Q1. Why is an incident timeline built from artefacts first, with interviews used only afterwards?
Q2. db01 was 4 seconds ahead of true time during an incident. Its error is logged at 09:14:06 and app01 logs its failure at 09:14:04. What is the real order and why does it matter?
Q3. An incident lasted 33m50s: 17m09s to detect, 7m35s to diagnose, 6m37s to restore. Detection was bounded by a synthetic check running every 15 minutes. What does the timeline decomposition give you that a merged narrative does not?
Q4. While assembling the timeline you find a log line your explanation does not account for. What should you do?
Q5. Which of these belong on the response timeline rather than the system timeline? Select all that apply.
Q6. A twenty-minute interval in the timeline with no entries at all is itself a finding worth recording.
Passing score: 75%. Answers are checked in this browser.