The Shell Crossover
The Shell Crossover, Part 8: Logs and Troubleshooting from the macOS Terminal
Translate Event Viewer habits to macOS terminal troubleshooting with unified logging, log show, log stream, predicates, install.log, and tail.
Windows administrators often begin troubleshooting in Event Viewer or with Get-WinEvent. On macOS, the closest starting point is the unified logging system and the log command.
Traditional log files still exist, but many modern macOS diagnostics are not stored as plain text files. You need both patterns: log show and log stream for unified logs, and tail for traditional files such as /var/log/install.log.
The troubleshooting map
| Windows habit | macOS terminal equivalent | Use case |
|---|---|---|
| Event Viewer | log show | Historical unified log review. |
| Live event view | log stream | Watch new unified log messages. |
Get-WinEvent -FilterHashtable | log --predicate | Filter by process, subsystem, category, or message. |
| Text log tailing | tail -f | Watch traditional text logs. |
| Export event data | log show --style json | Capture logs for review or escalation. |
| Application install logs | /var/log/install.log | Package install and software update history. |
Historical unified logs with log show
Use log show when you need historical data.
log show --last 1h --style compactThat is usually too noisy. Add a predicate.
log show --last 1h --predicate 'process == "mdmclient"' --style compactPredicates are the macOS logging equivalent of a focused event query. You can filter by fields such as process, subsystem, category, and event message. NSPredicate syntax accepts both AND and &&; AND is used throughout for readability.
log show --last 30m \
--predicate 'process == "mdmclient" AND eventMessage CONTAINS[c] "profile"' \
--style compactUse --info when the messages you need are at the info level.
log show --last 30m --info --predicate 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient"' --style compactUse --debug only when you need that level and understand the volume.
Live unified logs with log stream
Use log stream when you need to watch new events as they occur.
log stream --predicate 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient"' --style compactFor install-related troubleshooting:
log stream --predicate 'process == "installer" OR process == "installd"' --style compactA common mistake is using log stream when you need history. log stream starts watching from now. Use log show when the event already happened.
Traditional logs still matter
Some high-value logs are still plain text. The most common admin example is /var/log/install.log.
tail -f /var/log/install.logSearch recent install activity:
grep -i "example" /var/log/install.logCapture the last 200 lines for review:
tail -n 200 /var/log/install.log > /tmp/install-last-200.logPowerShell can do the same work.
Get-Content -Path "/var/log/install.log" -Tail 200For live tailing in PowerShell:
Get-Content -Path "/var/log/install.log" -WaitMDM troubleshooting example
When investigating MDM behavior, start with mdmclient, but do not stop at process-only filtering. For MDM protocol activity, filtering on the com.apple.ManagedClient subsystem reduces noise from unrelated mdmclient activity.
log show --last 2h --info \
--predicate 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient"' \
--style compactAdd a message filter when the output is too broad.
log show --last 2h --info \
--predicate 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient" AND eventMessage CONTAINS[c] "InstallProfile"' \
--style compactPowerShell wrapper:
$Predicate = 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient" AND eventMessage CONTAINS[c] "InstallProfile"'
& /usr/bin/log show --last 2h --info --predicate $Predicate --style compactExport JSON for deeper review.
$Predicate = 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient"'
$OutputPath = Join-Path $HOME "Desktop/mdmclient-log.json"
& /usr/bin/log show --last 2h --info --predicate $Predicate --style json |
Set-Content -Path $OutputPathSoftware installation example
For package installs, check both the traditional install log and unified logs.
tail -n 100 /var/log/install.loglog show --last 1h \
--predicate 'process == "installer" OR process == "installd"' \
--style compactIf you know the package identifier, combine log review with receipt inspection.
pkgutil --pkg-info com.example.package
pkgutil --files com.example.package | headLaunchDaemon troubleshooting example
Start with the job state.
sudo launchctl print system/dev.admincrossover.example.heartbeatThen inspect launchd messages that mention the label.
log show --last 1h \
--predicate 'process == "launchd" AND eventMessage CONTAINS[c] "dev.admincrossover.example.heartbeat"' \
--style compactCheck the stdout and stderr paths from the LaunchDaemon plist.
tail -n 50 /var/log/admincrossover-heartbeat.log
tail -n 50 /var/log/admincrossover-heartbeat.errThe fastest LaunchDaemon fixes usually come from three checks:
sudo plutil -lint /Library/LaunchDaemons/dev.admincrossover.example.heartbeat.plist
ls -l /Library/LaunchDaemons/dev.admincrossover.example.heartbeat.plist
sudo launchctl print system/dev.admincrossover.example.heartbeatReviewing a collected log archive
When you receive a .logarchive from another Mac, query it with log show --archive instead of querying the live local log store.
log show --archive "$HOME/Desktop/incident.logarchive" \
--last 2h \
--predicate 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient"' \
--style compactThis pattern is useful when logs were collected with log collect, included in a sysdiagnose, or gathered from a remote endpoint for offline review.
Capturing evidence for escalation
Create a folder, capture focused logs, and include command output.
case_dir="$HOME/Desktop/admincrossover-log-review-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$case_dir"
log show --last 2h --info \
--predicate 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient"' \
--style json > "$case_dir/mdmclient.json"
tail -n 300 /var/log/install.log > "$case_dir/install-last-300.log"
sw_vers > "$case_dir/sw_vers.txt"
profiles status -type enrollment > "$case_dir/profiles-status.txt" 2>&1PowerShell version:
$CaseDir = Join-Path $HOME ("Desktop/admincrossover-log-review-{0}" -f (Get-Date -Format "yyyyMMdd-HHmmss"))
New-Item -ItemType Directory -Path $CaseDir -Force | Out-Null
$Predicate = 'process == "mdmclient" AND subsystem == "com.apple.ManagedClient"'
& /usr/bin/log show --last 2h --info --predicate $Predicate --style json |
Set-Content -Path (Join-Path $CaseDir "mdmclient.json")
Get-Content -Path "/var/log/install.log" -Tail 300 |
Set-Content -Path (Join-Path $CaseDir "install-last-300.log")
& /usr/bin/sw_vers | Set-Content -Path (Join-Path $CaseDir "sw_vers.txt")
& /usr/bin/profiles status -type enrollment *> (Join-Path $CaseDir "profiles-status.txt")A note on sysdiagnose
sysdiagnose is a collection mechanism, not the first troubleshooting step. It gathers a broad diagnostic package that can be useful for escalation, but it is not a substitute for a focused log query.
Use focused logs first. Use sysdiagnose when Apple, a vendor, or your escalation path asks for a full diagnostic collection.
The operating rule
Use log show for history, log stream for live observation, and tail for traditional text logs. Build predicates early. Capture evidence to files when you need peer review, vendor support, or a repeatable troubleshooting record.