Unified Logs: macOS tracev3 System Logging
macOS Unified Logs (tracev3 plus uuidtext) record logins, sudo, TCC, Gatekeeper, installs and device activity with sub-second timestamps.
- Location
- /private/var/db/diagnostics/
- Proves
- What processes and subsystems reported, and when: logins, privilege use, security checks, devices
- Timestamps
- Mach time converted via timesync records; log show prints ISO 8601 with UTC offset
- Access
- root (admin group can read the store); log collect needs sudo
- Retention
- Size-based rotation, typically days to a few weeks
- Collection
- log collect, sysdiagnose, mac_apt, UAC, Aftermath
Tools
Compare all toolsWhat it is
The Unified Logging system (introduced in macOS 10.12 Sierra) is the central log for the kernel, Apple daemons and third-party code that uses os_log. logd writes compressed binary .tracev3 files; the human-readable format strings are not stored in each entry but in separate uuidtext and shared-cache (dsc) files keyed by the UUID of the binary that logged.
For an investigator it is the richest time-ordered record on a Mac: authentication, sudo, TCC decisions, Gatekeeper and XProtect activity, installs, disk mounts and network changes all land here. Its limits are equally important: quota-based rotation, levels that are never persisted, and <private> redaction.
Where it lives
| Path | Content | Notes |
|---|---|---|
/private/var/db/diagnostics/Persist/ | Main .tracev3 store | Up to roughly 10.5 MB per file (Mandiant observation) |
/private/var/db/diagnostics/Special/ | Lower-volume entries with their own time-to-live | Often older than Persist |
/private/var/db/diagnostics/Signpost/ | Performance signposts | Shown only with --signpost |
/private/var/db/diagnostics/HighVolume/ | High-volume data, when used | Often empty |
/private/var/db/diagnostics/timesync/ | Boot and clock correlation records | Needed to convert timestamps |
/private/var/db/uuidtext/00 to FF, dsc/ | Format strings and image paths, shared cache strings | Needed to render messages |
*.logarchive | Portable bundle from log collect or sysdiagnose | Self-contained |
The diagnostics directory is owned by root:admin with mode 750 on current releases; collect as root, and give the terminal Full Disk Access if the same run also copies TCC-protected user data. Entries that exist only in memory (most info and debug messages) are lost at shutdown.
What it proves
- Which process (
processImagePath) and which library (senderImagePath) emitted a message, with PID, thread and user ID. - Sequences of security events:
sudouse, SSH and local logins, TCC prompts and results, Gatekeeper assessments, XProtect Remediator scans, configuration profile installs. - Device and volume activity (USB mass storage, DiskArbitration mounts), network and Wi-Fi changes.
- Boot sessions (
bootUUID) and the time window actually covered by the store.
It does not prove absence. A missing entry can mean the level was not persisted, the value was redacted, the message wording changed between releases, or the file rotated out.
Key fields
Field names as emitted by log show --style json or ndjson:
| Field | Meaning |
|---|---|
timestamp | Wall-clock time with UTC offset, microsecond precision |
machTimestamp | Raw Mach time of the entry |
bootUUID | Boot session the entry belongs to |
processImagePath, processID | Process that logged |
senderImagePath, senderImageUUID | Binary or library that emitted the message |
subsystem, category | Reverse-DNS subsystem and category, e.g. com.apple.TCC |
messageType | Default, Info, Debug, Error, Fault |
eventType | e.g. logEvent, activityCreateEvent, signpostEvent |
eventMessage, formatString | Rendered text and the template it came from |
userID, threadID, activityIdentifier, traceID | Context for correlation |
timezoneName | Time zone recorded with the entry |
formatString is useful when values are <private>: the template still shows what kind of event happened.
Timestamps
Entries store Mach time values, not an epoch. The timesync records map each boot's Mach time to wall-clock time; on Apple silicon the raw value is in timebase ticks rather than nanoseconds, which is why third-party parsers need the timesync folder. The result is a UTC instant.
On current macOS, log show does not follow the TZ environment variable. Without --timezone, it prints each entry in the time zone recorded when it was written. Force UTC for timelines:
log show --archive case.logarchive --timezone UTC --style ndjson \
--start "2026-09-20 00:00:00" --end "2026-09-21 00:00:00" > day.ndjson
--start and --end accept YYYY-MM-DD, YYYY-MM-DD HH:MM:SS or a value with an explicit offset (HH:MM:SSZZZZZ).
Retention
Rotation is by size, not age: logd deletes the oldest .tracev3 files to keep the store within its budget, so a busy Mac may hold only days while a quiet one holds weeks. Mandiant observed about 52 Persist files of about 10.5 MB each (roughly 550 MB in total). Special entries carry a time-to-live and can outlast Persist. sudo log erase deletes the main store and in-flight data, and --all also removes TTL, fault and error content.
Collection
# Live: whole store (preferred), or a time slice
sudo log collect --output /Volumes/EVIDENCE/host.logarchive
sudo log collect --output /Volumes/EVIDENCE/last3d.logarchive --last 3d
# Offline store (recovery, mounted image): build an archive from a copied tree
sudo log collect --directory /Volumes/Image/private/var/db/diagnostics \
--output ./case.logarchive
--directory needs a uuidtext folder next to the diagnostics folder, as on a real volume.
- sysdiagnose includes a
.logarchive. - UAC copies
*.tracev3,uuidtextandtimesync(files/logs/macos_unified_logs.yaml). - mac_apt
UNIFIEDLOGEXPORTexportsdiagnosticsanduuidtextfrom an image for parsing with macos-UnifiedLogs. - Aftermath does not copy the store; it runs a set of
log showpredicates (sudo failures, logins, TCC, SSH, screen sharing, XProtect Remediator, manual profile installs) and accepts your own with--logs.
Hash the archive contents: a .logarchive is a directory.
Parsing
log show/log statson a Mac with the same or newer macOS: the reference implementation.- macos-UnifiedLogs (Mandiant, Rust): github.com/mandiant/macos-UnifiedLogs. The
unifiedlog_iteratorexample handles live systems, log archives and single files:
unifiedlog_iterator --mode log-archive --input case.logarchive \
--output case.jsonl --format jsonl
- Plaso
unified_loggingparser (not in the default macOS preset, name it explicitly):log2timeline.py --parsers unified_logging --storage-file ul.plaso ./case.logarchive.
Starter predicates (adjust to the target release):
log show --archive case.logarchive --timezone UTC --style syslog --predicate '
(process == "sudo" AND eventMessage CONTAINS "COMMAND=")
OR process == "sshd"
OR subsystem == "com.apple.TCC"
OR process == "syspolicyd"
OR subsystem == "com.apple.DiskArbitration.diskarbitrationd"'
Unified Log Parser opens a .logarchive, the diagnostics and uuidtext folders or a log show export in the browser, applies DFIR triage rules and exports CSV or Timesketch; nothing is uploaded.
Investigator tips
- Record the earliest and latest timestamp per boot (
log stats, or first and last lines) before stating that "nothing happened" in a window. - Always keep
uuidtextwith the tracev3 files. Without it, cross-platform tools render incomplete messages. - On macOS 26 the
logtool records its own runs under subsystemcom.apple.log, including the parent process and arguments. Look there forlog eraseor broadlog showcommands run by an intruder. - A sharp start of history right after a boot on a quiet Mac, with older data in
Specialbut notPersist, is worth checking against a possible erase. - Validate third-party parser output against
log showfor anything you will testify to, and note the parser version. - Pivot from here to the dedicated pages: sudo logs, USB devices, Gatekeeper and XProtect, TCC.db. The Unified Logs guide has more predicates.