Skip to content

LogsExecutionUser activity

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

What 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

PathContentNotes
/private/var/db/diagnostics/Persist/Main .tracev3 storeUp to roughly 10.5 MB per file (Mandiant observation)
/private/var/db/diagnostics/Special/Lower-volume entries with their own time-to-liveOften older than Persist
/private/var/db/diagnostics/Signpost/Performance signpostsShown only with --signpost
/private/var/db/diagnostics/HighVolume/High-volume data, when usedOften empty
/private/var/db/diagnostics/timesync/Boot and clock correlation recordsNeeded to convert timestamps
/private/var/db/uuidtext/00 to FF, dsc/Format strings and image paths, shared cache stringsNeeded to render messages
*.logarchivePortable bundle from log collect or sysdiagnoseSelf-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: sudo use, 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:

FieldMeaning
timestampWall-clock time with UTC offset, microsecond precision
machTimestampRaw Mach time of the entry
bootUUIDBoot session the entry belongs to
processImagePath, processIDProcess that logged
senderImagePath, senderImageUUIDBinary or library that emitted the message
subsystem, categoryReverse-DNS subsystem and category, e.g. com.apple.TCC
messageTypeDefault, Info, Debug, Error, Fault
eventTypee.g. logEvent, activityCreateEvent, signpostEvent
eventMessage, formatStringRendered text and the template it came from
userID, threadID, activityIdentifier, traceIDContext for correlation
timezoneNameTime 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, uuidtext and timesync (files/logs/macos_unified_logs.yaml).
  • mac_apt UNIFIEDLOGEXPORT exports diagnostics and uuidtext from an image for parsing with macos-UnifiedLogs.
  • Aftermath does not copy the store; it runs a set of log show predicates (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 stats on a Mac with the same or newer macOS: the reference implementation.
  • macos-UnifiedLogs (Mandiant, Rust): github.com/mandiant/macos-UnifiedLogs. The unifiedlog_iterator example handles live systems, log archives and single files:
unifiedlog_iterator --mode log-archive --input case.logarchive \
  --output case.jsonl --format jsonl
  • Plaso unified_logging parser (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 uuidtext with the tracev3 files. Without it, cross-platform tools render incomplete messages.
  • On macOS 26 the log tool records its own runs under subsystem com.apple.log, including the parent process and arguments. Look there for log erase or broad log show commands run by an intruder.
  • A sharp start of history right after a boot on a quiet Mac, with older data in Special but not Persist, is worth checking against a possible erase.
  • Validate third-party parser output against log show for 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.

See also