Since the introduction of macOS Sierra, Apple has fundamentally overhauled how system logs are managed, transitioning away from traditional flat-text files (like /var/log/system.log) to the Unified Logging System (ULS). The ULS is a highly compressed, binary-format datastore managed by the logd daemon, offering incredible performance and privacy protections.
For macOS administrators and security analysts, traditional utilities like grep or tail are no longer sufficient for parsing system events. To hunt for advanced threats, track application executions, or debug system crashes, you must master the log command-line utility. This guide explains how to leverage the log show command with advanced predicates to extract actionable telemetry from macOS.
The Architecture of Unified Logging
The Unified Logging System organizes data into a highly structured format. Every log entry contains rich metadata, including:
- Subsystem: The specific component generating the log (e.g.,
com.apple.TCCfor privacy controls). - Category: A functional grouping within the subsystem (e.g.,
accessorauth). - Process ID (PID): The specific process that generated the event.
- Log Type: The severity of the log (e.g., Default, Info, Debug, Error, Fault).
Because the logs are stored in a binary format, they can only be read using the macOS Console.app or the log Terminal command.
Step 1: Basic Usage of log show
The log show command dumps historical logs from the binary datastore. By default, running log show without arguments will attempt to dump millions of lines of text to your terminal, which is entirely unmanageable.
You must restrict the output using timeframes and limits. For example, to view logs generated only within the last 10 minutes:
log show --last 10m
To view logs from a specific date and time range:
log show --start "2024-10-25 09:00:00" --end "2024-10-25 09:15:00"
Step 2: Using Predicates for Advanced Filtering
The true power of the log command lies in the --predicate flag. Predicates use the NSPredicate syntax (borrowed from Objective-C) to perform highly specific SQL-like queries against the log metadata.
Hunting for Application Executions
If you want to track which applications have been launched by the user (a common requirement for threat hunting), you can filter for logs generated by the LaunchServices subsystem.
log show --predicate 'subsystem == "com.apple.launchservices" AND category == "application"' --last 1h
Hunting for Privilege Escalation (sudo usage)
To audit when administrators are elevating privileges via the sudo command, you can filter by the specific process name.
log show --predicate 'process == "sudo"' --last 24h
Hunting for Privacy Prompt Approvals (TCC)
Transparency, Consent, and Control (TCC) manages access to the microphone, camera, and Full Disk Access. Malware often attempts to bypass or manipulate these prompts. You can query the TCC subsystem to review all privacy-related events:
log show --predicate 'subsystem == "com.apple.TCC"' --last 1h
Step 3: Extracting Logs for External SIEM Analysis
If you are aggregating logs into a centralized Security Information and Event Management (SIEM) platform (like Splunk or Elastic), reading the default text output is inefficient. The log command allows you to output the data in structured JSON format, which is easily ingested by log forwarders.
log show --predicate 'process == "sudo"' --last 1h --style json > sudo_logs.json
The resulting sudo_logs.json file will contain deeply nested JSON objects, exposing every attribute of the log event, including thread IDs, Mach continuous time, and the exact executable path.
Step 4: Real-Time Log Streaming
While log show is used for historical querying, the log stream command is used for live, real-time debugging. This replaces the legacy tail -f /var/log/system.log workflow.
For example, if you are developing an application or deploying an MDM profile and want to see the immediate result of an MDM command, you can stream the MDM client logs in real-time:
log stream --predicate 'subsystem == "com.apple.ManagedClient"'
The terminal will remain open, printing new log entries exactly as they are generated by the MDM daemon.
Step 5: Managing Privacy and <private> Tags
By default, Apple redacts sensitive information (like filenames, IP addresses, or usernames) in the Unified Logging System to protect user privacy. Redacted fields appear in the output as <private>.
During active incident response or debugging, this redaction can hide critical indicators of compromise (IoCs). To reveal these private strings, you must install a specific configuration profile to disable log redaction, which requires MDM deployment or SIP disabling (which is not recommended). Alternatively, you can use the sudo command when running log collect to gather a comprehensive sysdiagnose archive, which often contains less-redacted historical data when opened in Console.app on the same machine.
Conclusion
The macOS Unified Logging System is a massively powerful, highly structured forensic datastore. By abandoning legacy text-parsing tools and embracing NSPredicate syntax via the log show and log stream commands, security analysts can perform surgical threat hunting and extract rich, actionable telemetry from macOS endpoints.