How to Configure Unified Logging Predicates, log stream Filtering, and Subsystem Tracing in macOS

When debugging deep system faults, intermittent Bluetooth connectivity drops, TCC privacy permission denials, or kernel extension panics on macOS, legacy file-based logging (such as inspecting /var/log/system.log) is entirely inadequate. Modern macOS versions route all diagnostic messages through the Apple Unified Logging System (os_log). Designed for high performance and low CPU overhead, unified logging captures events across userland processes, dynamic frameworks, and Darwin kernel drivers.

The unified logging engine writes structured events into ring buffers in RAM and highly compressed binary trace files (.tracev3). However, because macOS generates tens of thousands of log entries every second, attempting to read raw logs without strict filtering creates overwhelming visual noise. By mastering NSPredicate syntax, subsystem tracing, and the command-line log stream and log collect utilities, system engineers and developers can target precise kernel and application events in real time.

Architectural Overview: The macOS Unified Logging Pipeline

The unified logging subsystem eliminates disk I/O bottlenecks by categorising and staging messages dynamically:

Log Level Persistence Lifecycle Default Collection Behaviour Diagnostic Purpose
Default Memory buffer; written to disk during high-activity flushes. Always captured and stored in circular trace buffers. Essential application milestones and standard operational telemetry.
Info Memory buffer only. Purged automatically unless captured via live stream or explicit diagnostic collect. Detailed execution tracking for debugging normal behavior.
Debug Memory buffer only (Disabled by default). Suppressed from system buffers unless explicitly activated via log config. High-volume developer troubleshooting data.
Error Immediately persisted to disk. Saved to permanent .tracev3 files for post-mortem analysis. Localized operation failures (e.g. file not found, network handshake drop).
Fault Immediately persisted to disk; triggers crash reporting capture. Always saved across reboot cycles. Critical system or process-level panics, memory corruption, or fatal traps.

Streaming Real-Time System Events with log stream and NSPredicates

The log stream CLI command attaches to the active in-memory logging queue, allowing live filtering via Apple’s Objective-C / Swift NSPredicate syntax.

Step 1: Stream Events by Process and Subsystem

To inspect security and keychain events originating from Apple’s security daemon (securityd):

# Stream securityd events in compact console format
log stream --predicate 'subsystem == "com.apple.securityd"' --style compact

Step 2: Isolate Dynamic TCC Privacy Denials

When an application attempts to access the microphone, camera, or full disk storage without user consent, the Transparency, Consent, and Control (TCC) subsystem generates an error. Monitor TCC decisions live:

# Filter live TCC authorization evaluations
log stream --predicate 'subsystem == "com.apple.TCC" and eventMessage CONTAINS[c] "Prompting"' --style compact

Step 3: Compound Logical Filtering

Filter for high-severity faults occurring strictly within network or Wi-Fi subsystems:

# Stream Error and Fault messages from Wi-Fi and Network components
log stream --predicate '(subsystem CONTAINS "network" OR subsystem CONTAINS "wifi") AND (messageType == error OR messageType == fault)'

Configuring Subsystem Logging Modes via log config

By default, macOS suppresses high-volume Info and Debug messages to conserve memory. To inspect detailed internal state transitions, enable debug level tracing for a target subsystem.

Step 1: Check Active Subsystem Logging Policy

# Query active configuration for Bluetooth subsystem
log config --status com.apple.bluetooth

Step 2: Elevate Logging to Debug Mode

Activate persistent debug logging across the target subsystem:

# Enable Info and Debug messages for Bluetooth
sudo log config --mode "level:debug" --subsystem com.apple.bluetooth

Now, running log stream --predicate 'subsystem == "com.apple.bluetooth"' --level debug will display the complete, unthrottled low-level HCI packet trace.

Step 3: Reset Logging to Default Production Settings

Once troubleshooting concludes, restore default behavior to prevent excessive memory consumption:

# Reset subsystem logging state to factory default
sudo log config --reset --subsystem com.apple.bluetooth

Collecting Historical Diagnostics with log collect

When investigating a crash or kernel panic that occurred in the past, capture the historical compressed trace files into an offline .logarchive bundle.

Step 1: Extract Log Trace Archives

# Archive all system logs generated over the last 30 minutes
sudo log collect --last 30m --output ~/Desktop/crash_analysis.logarchive

Step 2: Query the Offline Log Archive

Inspect the offline bundle on any Mac using the log show utility or the GUI Console.app:

# Search historical archive for kernel panic markers
log show ~/Desktop/crash_analysis.logarchive --predicate 'eventMessage CONTAINS "panic"' --style compact

Troubleshooting macOS Unified Logging Failures

Consult this diagnostic reference table to debug syntax errors and missing telemetry:

Issue Underlying Root Cause Remediation Procedure
log: predicate syntax error Incorrect string quotes or case mismatch in NSPredicate field identifiers. Ensure string values are wrapped in quotes (e.g. subsystem == "com.apple.foo"). Use CONTAINS[c] for case-insensitive matching.
Log stream outputs <private> instead of user data or file paths. macOS redacts personal identifiable information (PII) by default inside string interpolations (os_log("%{private}@", path)). Install an authorized Apple OS configuration profile enabling private data logging, or run in the iOS Simulator where PII redaction is disabled.
Debug-level messages do not appear in log stream. The target subsystem has not been configured to emit debug entries via log config. Execute sudo log config --mode "level:debug" --subsystem <subsystem_name> and include --level debug in your log stream invocation.
log collect generates a massive multi-gigabyte file. Omitting the --last or --start time boundary forced the utility to ingest days of system traces. Always bound your collection window using --last 15m, --last 1h, or explicit timestamp formats: --start "2026-10-24 01:00:00".

Mastering the macOS Unified Logging System, NSPredicate filters, and log stream commands gives administrators surgical visibility into the Darwin operating system, rapidly demystifying obscure kernel failures and application crashes.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.