macOS Unified Logs Forensics: A Practical Guide
What macOS Unified Logs record, where tracev3, uuidtext and timesync live, what they can and cannot prove, and a workflow to read them for an investigation.
TL;DR. Unified Logs are the most detailed timeline a Mac keeps: authentication, sudo, privacy (TCC) decisions, Gatekeeper, launch agents, remote access and disk mounts, each with a process, a subsystem and a microsecond timestamp. The catch is that an entry is split across three kinds of files — .tracev3, uuidtext/dsc and timesync — and you need all of them. This guide explains what is where and how to work through it; the Unified Log Parser reads the lot in your browser.
What the Unified Logging system is
Since macOS 10.12 Sierra, Apple routes almost all logging through one system. The kernel, Apple's daemons and any third-party code calling os_log send entries to logd, which writes them to compressed binary files. Syslog-style text files under /var/log still exist, but most of what matters in an investigation lives in the unified store.
Each entry carries:
- the time, microsecond precision once converted;
- the process (image path and PID), thread and user ID;
- the sender image: the library or framework that logged (often not the process itself);
- a subsystem and category in reverse-DNS style, for example
com.apple.TCC/access; - a type: Default, Info, Debug, Error or Fault for log messages, plus activities, signposts and state dumps;
- the message, rendered from a format string and the values recorded with the entry.
Where the files live
| Path | Content |
|---|---|
/private/var/db/diagnostics/Persist/ | Main store of .tracev3 files |
/private/var/db/diagnostics/Special/ | Entries with their own time-to-live (often older than Persist) |
/private/var/db/diagnostics/Signpost/ | Performance signposts |
/private/var/db/diagnostics/HighVolume/ | High-volume data, usually empty |
/private/var/db/diagnostics/timesync/ | Boot and clock correlation records |
/private/var/db/uuidtext/XX/… | Format strings, one file per binary UUID |
/private/var/db/uuidtext/dsc/… | Strings of the dyld shared cache |
A .logarchive produced by log collect or sysdiagnose holds the same folders plus logdata.LiveData.tracev3, the in-memory buffer at collection time. The tracev3 format article goes through the binary layout.
Why you always need three kinds of files
A .tracev3 entry does not store its text. It stores a reference to a format string (an offset into a uuidtext file or into the shared-cache dsc file) and the raw argument values. Without the string files, a parser can only show numbers and fragments; entries are "unresolved". Times are stored as boot-relative Mach continuous time, converted to wall-clock time with the timesync records; without them, times are approximate at best.
So the rule for collection is simple: take diagnostics and uuidtext, or a .logarchive, which has both.
What Unified Logs can prove
- Authentication and privilege use:
sudocommands with the target user and the full command line, failed passwords,su, authorization rights granted byauthd, local account changes. - Remote access: SSH (
sshd,sshd-session) and Screen Sharing sessions, with the remote address when logged. - Privacy decisions: TCC prompts and results — for example Full Disk Access granted to Terminal — under subsystem
com.apple.TCC. - Execution context: Gatekeeper assessments and overrides by
syspolicyd, XProtect and XProtect Remediator activity, quarantine attributes, LaunchServices application launches. - Persistence: launch agents and daemons being loaded, Background Task Management registering login items.
- Devices: volumes mounted and ejected by
diskarbitrationd, USB devices attaching.
The triage queries article lists concrete searches for each.
What they cannot prove
- Absence. A missing entry can mean the store rotated, the level was never persisted (most Info and Debug entries live only in memory), the value was redacted, or Apple changed the wording in that release.
- Redacted values.
<private>means the value was hidden when it was logged. No parser can recover it; the format string still shows what kind of event it was. - Exact wording across versions. Messages are not an API. A search tuned on one macOS release can miss events on another; prefer subsystem, category and process filters, and read around each hit.
A workflow that holds up
- Collect early, with
uuidtextandtimesync, and hash what you collected. - Record coverage: the first and last entry per boot session, and any gap, before you say "nothing happened".
- Narrow by time around known events (a malicious download, a login), then widen.
- Filter by process and subsystem rather than by message text alone.
- Read around every hit: the entries from the same PID a few seconds before and after usually explain it.
- Confirm key entries with
log showon a Mac for anything that goes into a report, and note the parser and version you used.
Doing it in the browser
The Unified Log Parser decodes tracev3 files from a .logarchive, a copied /private/var/db tree or a UAC / Velociraptor collection, and also opens log show exports. It gives you a time range with a density strip, filters by type, process, subsystem and PID, curated DFIR presets, and exports to CSV, NDJSON and Timesketch. Everything runs locally with WebAssembly; the walkthrough uses the synthetic FIN-MBP-03 sample.
Further reading: Mandiant's macos-UnifiedLogs project and the libyal Apple Unified Logging format notes document the formats in depth.