System

Unified Logs

Overview

The macOS Unified Logging System (ULS) is the system-wide structured logging infrastructure introduced in macOS 10.12 Sierra. It replaced the legacy Apple System Log (ASL) and syslog frameworks and is now the authoritative event record for the kernel, all Apple system frameworks, and most third-party applications. Every process on a modern macOS system writes to ULS; no other artifact offers the same breadth of cross-subsystem correlation.

Architecture

ULS has four physical components that together form a self-contained, decodable archive:

ComponentLocationPurpose
tracev3 chunks/var/db/diagnostics/ (subdirs)Binary log records; opaque without the format-string databases
uuidtext format strings/var/db/uuidtext/Per-binary format strings keyed by UUID; required to render messages
Dynamic Shared Cache (dsc)/var/db/uuidtext/dsc/Shared-library format strings; ship once, referenced by many binaries
timesync anchors/var/db/diagnostics/timesync/Mach-time-to-wall-clock calibration records, one per boot

The tracev3 files contain compressed, binary-encoded log entries that reference format strings by UUID rather than embedding them. Rendering a human-readable message requires the uuidtext database for that UUID. The timesync anchors convert the Mach absolute time stored in each record to a calendar timestamp — without the correct anchor, timestamps cannot be recovered.

.logarchive bundles are self-contained: log collect copies all four components into a single directory bundle, making the bundle independently renderable on any macOS host.

Ring-Buffer Retention

The store is partitioned into priority tiers. The Persist/ tier survives reboots; the HighVolume/ tier is overwritten most aggressively.

TierPathTypical Retention
Persist/var/db/diagnostics/Persist/Days to a few weeks
Special/var/db/diagnostics/Special/Extended retention for high-priority subsystems
Signpost/var/db/diagnostics/Signpost/Performance intervals; short retention
HighVolume/var/db/diagnostics/HighVolume/Hours; high-frequency debug output
Live data/var/db/diagnostics/logdata.LiveData.tracev3Current write buffer
Timesync/var/db/diagnostics/timesync/One file per boot; retained longer

On a typical active laptop the Persist tier retains approximately one to two weeks of events. Under sustained high activity the window may be as short as a few days. ULS is not a long-term audit log.

Retention is hours to weeks, not months

Do not assume historical events survive in ULS. The ring-buffer overwrites itself continuously. Events older than two to three weeks are likely lost unless a .logarchive was captured contemporaneously. Plan collection to run as early as possible in any investigation timeline.

Forensic Significance

ULS is the only macOS artifact that can answer all of the following questions from a single source:

QuestionSubsystem
Was this binary allowed to execute, and what was the AMFI verdict?com.apple.AMFI, com.apple.kernel
What did the user authenticate to, and when?com.apple.loginwindow, com.apple.securityd, com.apple.authorization
What hostnames did mDNSResponder resolve during the incident window?com.apple.network, com.apple.mDNSResponder
Which USB devices were attached, and at what exact time?com.apple.iokit.IOUSBFamily, com.apple.usbmuxd
Did Gatekeeper or XProtect block or allow this download?com.apple.syspolicy, com.apple.xprotect, com.apple.LaunchServices.quarantine
Which app requested a TCC permission, and was it granted?com.apple.TCC

File Locations

ArtifactPathSIP ProtectedRoot RequiredNotes
Persist chunk store/var/db/diagnostics/Persist/YesYesPrimary forensic target; survives reboots
Special chunk store/var/db/diagnostics/Special/YesYesPriority-retained subsystems
Signpost store/var/db/diagnostics/Signpost/YesYesPerformance trace intervals
HighVolume store/var/db/diagnostics/HighVolume/YesYesShort-lived; collect immediately
Live write buffer/var/db/diagnostics/logdata.LiveData.tracev3YesYesActive file; may be locked
Timesync anchors/var/db/diagnostics/timesync/YesYesOne file per boot; critical for timestamps
UUID format strings/var/db/uuidtext/YesYesKeyed by binary UUID
DSC shared strings/var/db/uuidtext/dsc/YesYesShared library format strings

All paths are under /var/db/ and protected by System Integrity Protection (SIP). Read access requires root on a live system. On a mounted disk image, standard read permissions apply once the image is attached with appropriate tools.

File Naming

tracev3 chunk files within each subdirectory carry a UUID-derived filename. The timesync directory contains one .timesync file per boot, named with a timestamp. There is no persistent sequential numbering analogous to FSEvents record files.


Subsystem Forensic Value

The table below maps the six predicate presets collected by macfor to the subsystems they target and their primary forensic use cases.

PresetKey Subsystems / ProcessesWhat It CapturesPrimary Use Cases
execcom.apple.AMFI, com.apple.kernel, com.apple.launchdProcess execution decisions, code-signing verdicts, AMFI allow/deny, launchd service activationsMalware execution tracing; unsigned binary detection; privilege escalation via launchd
authcom.apple.loginwindow, com.apple.securityd, com.apple.authorization, com.apple.opendirectoryd, sudo, sshdConsole login/logout, screen lock/unlock, sudo invocations, SSH sessions, Open Directory lookups, keychain authorizationAccount activity reconstruction; lateral movement; privilege abuse
networkcom.apple.network, com.apple.network.connection, mDNSResponder, com.apple.networkextensionTCP/UDP connection establishment, DNS resolution, NE policy decisions, VPN tunnel eventsC2 identification; DNS-based IOC matching; network egress baseline
usbcom.apple.iokit.IOUSBFamily, com.apple.usbmuxd, messages containing USBMSC or IOUSBHostDeviceDevice attach/detach, vendor/product IDs, mass-storage enumeration, iOS device pairingData exfiltration via USB storage; device-timeline correlation; insider threat
xprotectcom.apple.xprotect, com.apple.syspolicy, com.apple.LaunchServices.quarantineXProtect signature matches, Gatekeeper notarisation checks, quarantine assessments, MRT actionsMalware detection tracing; Gatekeeper bypass evidence; download provenance
tcccom.apple.TCCPermission prompts, grant/deny decisions, service access eventsSpyware/RAT permission tracing; MDM-managed grant audit; FDA escalation

Critical Forensic Caveats

Private data redaction cannot be reversed

<private> tokens in eventMessage fields are applied at write time by the logging subsystem. The unredacted value is never written to disk — not even in the raw tracev3 store. Retroactive recovery is impossible. The only way to obtain unredacted messages is to configure a com.apple.system.logging MDM profile with Enable-Private-Data = true before the events are generated. On a system without this profile, many forensically relevant message fields — including process arguments, filenames, and network addresses — will appear as <private>.

Always use --timezone UTC when rendering logs

log show renders timestamps in the host system's local timezone by default. Forensic output produced without --timezone UTC will have timestamps offset by the host TZ, which may be different from the source system's TZ. The rendered timestamps will be inconsistent across sessions on different investigators' machines. Always pass --timezone UTC; macfor does this automatically for all preset renders.

Boot UUID boundaries require per-boot timesync anchors

Each system boot generates a new Boot UUID. The tracev3 files partition by Boot UUID; Mach absolute time resets to zero at each boot. Correlating events across two boots requires the timesync anchor for each boot to convert Mach time to wall clock time. The log show CLI handles this automatically when operating on a .logarchive that includes the timesync directory. Manual cross-boot analysis against raw tracev3 files without the timesync data produces incorrect timestamps.


Predicate Presets

macfor renders six predicate-filtered NDJSON extracts. Each preset targets a specific forensic domain using an NSPredicate expression passed to log show --predicate.

PresetNSPredicateForensic Purpose
auth(subsystem == "com.apple.loginwindow") OR (subsystem == "com.apple.opendirectoryd") OR (subsystem == "com.apple.authorization") OR (subsystem == "com.apple.securityd") OR (process == "sudo") OR (process == "sshd")Authentication, session, and privilege escalation events
usb(subsystem == "com.apple.iokit.IOUSBFamily") OR (subsystem == "com.apple.usbmuxd") OR (eventMessage CONTAINS "USBMSC") OR (eventMessage CONTAINS "IOUSBHostDevice")USB device attachment, mass-storage enumeration, iOS pairing
network(subsystem == "com.apple.network") OR (subsystem == "com.apple.network.connection") OR (process == "mDNSResponder") OR (subsystem == "com.apple.networkextension")Network connections, DNS resolution, VPN/NE policy
exec(subsystem == "com.apple.kernel") OR (subsystem == "com.apple.AMFI") OR (subsystem == "com.apple.launchd") OR (eventMessage CONTAINS "exec")Process execution, AMFI code-signing verdicts, launchd activations
xprotect(subsystem BEGINSWITH "com.apple.xprotect") OR (subsystem == "com.apple.syspolicy") OR (subsystem == "com.apple.LaunchServices.quarantine")XProtect / Gatekeeper / MRT decisions on downloaded content
tcc(subsystem == "com.apple.TCC")TCC permission prompts and grant/deny decisions

Predicates are applied during log show rendering. They filter events before writing to NDJSON; no events outside the predicate are stored in the extract.


NDJSON Event Record Schema

Each line of a rendered NDJSON extract is a JSON object with the following fields:

FieldTypeSourceNotes
presetstringmacfor-addedOne of auth, usb, network, exec, xprotect, tcc
timestampstringlog showRFC 3339 with nanosecond precision, always UTC
subsystemstringlog showReverse-DNS bundle-style identifier
categorystringlog showSub-categorisation within the subsystem
processstringlog showShort process name
process_image_pathstringlog showFull path to process binary
sender_image_pathstringlog showLibrary or framework that emitted the entry
messagestringlog showRendered log message; may contain <private> tokens
message_typestringlog showOne of default, info, debug, error, fault
thread_iduint64log showMay be absent on older macOS versions
trace_iduint64log showCorrelates entries within an activity chain
activity_iduint64log showActivity tree root identifier
extraobjectmacfor-addedPassthrough map for any unrecognised log show fields

message_type values error and fault represent unexpected conditions; fault indicates a programmer error at the source. Filtering to these levels first is a useful triage shortcut when reviewing a large extract.


macfor Collection Details

What Is Collected

The system.unifiedlogs plugin produces three artifacts:

ArtifactEvidence Container PathDescription
Raw diagnostic storeunifiedlogs/raw/Verbatim copy of /var/db/diagnostics and /var/db/uuidtext with per-file SHA-256 hashes
Logarchive bundleunifiedlogs/logs.logarchive/Self-contained .logarchive generated by log collect; independently renderable
Event extractsunifiedlogs/events/<preset>.ndjsonSix predicate-filtered NDJSON files (one per preset)

Manifest Contents

The collection manifest (manifest.json) for this plugin records:

  • log CLI version (from log version)
  • Boot UUID (from logs.logarchive/Info.plist)
  • Collection window start and end timestamps
  • Per-file SHA-256 hashes for all raw store files
  • File count and total bytes per directory tier
  • Per-preset event counts and any preset-level errors

Collection Options

FlagDefaultDescription
--time-window720h (30 days)--last window passed to log collect
--logarchive-path <path>Offline mode: render presets against a pre-built .logarchive
--skip-rawfalseSkip raw /var/db/diagnostics + /var/db/uuidtext preservation
--skip-logarchivefalseSkip .logarchive generation
--skip-eventsfalseSkip all NDJSON preset rendering
--skip-auth / --skip-usb / etc.falseDisable individual presets

Requirements

Live collection requires root. The plugin detects privilege at runtime and emits a clear error if root is absent. Detection (macfor detect) reports the artifact as present even without root, so the privilege gap is visible in pre-collection reports.

macOS 10.15 Catalina through 15 Sequoia are supported. The plugin relies on Apple's log CLI, which Apple maintains for forward compatibility.


Investigation Recipes

Was a Specific Binary Allowed to Execute?

Use the exec preset, which captures AMFI verdicts and kernel-level execution events.

# Render the exec extract (macfor already does this during collection)
log show logs.logarchive \
  --style ndjson \
  --predicate '(subsystem == "com.apple.AMFI") OR (subsystem == "com.apple.kernel")' \
  --timezone UTC \
  | grep -i "mymalware"

In the NDJSON extract, look for message fields containing the binary path. AMFI messages include verdict strings such as allowing (permitted), denied (blocked), and Library Validation failed. A sequence of allowing verdict followed by execution with no subsequent crash indicates successful execution. A denied entry indicates SIP or AMFI blocked it.

Cross-reference with the xprotect preset if the binary was downloaded: a Gatekeeper assessment log entry will reference the same path within seconds of the AMFI verdict.

Which USB Devices Were Attached During a Suspicious Window?

Use the usb preset. Mass-storage devices produce IOUSBFamily attachment entries followed by USBMSC enumeration messages that carry vendor string, product string, and serial number.

# Filter usb.ndjson to a specific time window
cat unifiedlogs/events/usb.ndjson \
  | python3 -c "
import sys, json
for line in sys.stdin:
    e = json.loads(line)
    ts = e.get('timestamp','')
    if '2026-04-10T03' <= ts <= '2026-04-10T05':
        print(e['timestamp'], e['message'])
"

Key message patterns to look for in the usb extract:

PatternMeaning
IOUSBHostDevice::startDevice enumeration began
USBMSC: Vendor = ..., Product = ...Mass-storage class device; note vendor/product strings
setConfiguration: 1Device configuration set; attachment complete
IOUSBHostDevice::stopDevice detached

Vendor/product strings from USBMSC messages can be correlated against the USB device history in the devices.bluetooth artifact and macOS's IORegistry for a complete attachment timeline.

Did Gatekeeper Quarantine or Block This Download?

Use the xprotect preset. Gatekeeper (syspolicy) and the quarantine LaunchServices subsystem emit assessment entries whenever a downloaded item is opened.

# Search xprotect extract for a specific filename
grep -i "SuspiciousInstaller.pkg" unifiedlogs/events/xprotect.ndjson \
  | python3 -c "import sys, json; [print(json.dumps(json.loads(l), indent=2)) for l in sys.stdin]"

Key syspolicy message patterns:

Message PatternMeaning
assessmentd: verdict grantedGatekeeper allowed execution
assessmentd: verdict deniedGatekeeper blocked execution
XProtect: matched rule ...XProtect signature triggered
MRT: removing ...Malware Removal Tool acted on the item
com.apple.LaunchServices.quarantine: ... releasing quarantineQuarantine bit cleared (user clicked Open)

Combine the xprotect extract with the Quarantine Events database (~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2) for the download URL and originating process. The two artifacts provide complementary views: the SQLite database records the download origin; ULS records what Gatekeeper decided.


Version Differences

macOS VersionNotes
10.12 SierraULS introduced; replaces ASL
10.15 CatalinaBaseline for macfor support; Signpost chunks stable
11 Big Surcom.apple.networkextension subsystem added; Catalog v3 chunk extensions
12 MontereyIncreased <private> redaction scope by default across more subsystems
13 Venturacom.apple.xprotect.daemon subsystem added; additional XPC activity logging
14 SonomaMinor tracev3 chunk tweaks; handled transparently by log CLI
15 SequoiaNo breaking changes identified

Forward compatibility is inherited from Apple's log CLI, which Apple maintains across OS versions. New NDJSON fields from future macOS releases are absorbed into the extra passthrough map.


Known Limitations (v1)

LimitationDetail
No native Go tracev3 parsingThe plugin wraps Apple's /usr/bin/log. Raw tracev3 files in the evidence container cannot be decoded by macfor without the log CLI — they require macOS to render.
Live collection requires rootNon-root invocations produce a collection error. Offline mode (supplying a pre-built .logarchive) has no root requirement.
No custom predicate expressionsUser-supplied predicate strings are not supported in v1. Only the six built-in presets are available. Custom predicates are planned for v1.1.
No live streaminglog stream is not implemented. Collection captures a historical window; there is no provision for tailing the live log stream.
Offline disk-image reconstruction not supportedIf a .logarchive was not generated on the source host, macfor cannot construct one from a mounted disk image. The user must provide a pre-built .logarchive via --logarchive-path.
NDJSON volumelog show output is 10–30x the size of the source tracev3 data. On large stores, each preset extract may be several gigabytes. Use --skip-* flags to omit low-priority presets when storage is constrained.

Tool Support

ToolSupport
macforRaw store preservation + .logarchive + six NDJSON presets via system.unifiedlogs
Apple log CLI (/usr/bin/log)Native rendering; required by macfor for all decode operations
mandiant/macos-UnifiedLogsOpen-source Rust parser; can decode tracev3 without the log CLI
UnifiedLogReader (Yogesh Khatri)Python-based parser; broad format support
mac_aptFull macOS forensics platform; includes ULS module
AXIOM (Magnet)Commercial ULS parsing and timeline integration

References

Previous
System Information