As an IT admin, you’ve almost certainly had to check some form of log when investigating a problem. Logs tell the story of what’s happening on a system, so they can be enormously helpful in both troubleshooting issues and in learning why the system is behaving the way that it is.
In this guide, we’ll cover some of the basics of Apple’s unified logging system, go over how to read the logs using the
log command, and provide some practical examples of how to filter log messages to find the ones that are most important to you.
When unified logging was first introduced at WWDC 2016, it was a pretty radical and bold change. Until then, most—if not all—logs on Apple platforms were recorded using standard Unix logging systems, usually writing to flat files on disk.
But as Apple’s platforms became more and more complex over the years, the volume of logs they generated increased as well. When Apple introduced unified logging, it dubbed it “logging for the future.” It set out to create a common logging system for all of its platforms with the following goals:
- Provide a single efficient logging mechanism for both user and kernel mode.
- Maximize information collection with minimum observer effect. (In other words, collect as much data as possible while compromising performance as little as possible. Among other things, that meant compressing log data and providing robust ways to manage log message lifecycles.)
- Keep as much logging on as much of the time as possible.
- Design privacy into the system.
Many admins might still think of looking for log messages in the common Unix flat files in
/var/log/. And it’s true, there are still some flat log files written there; for example,
/var/log/install.log contains useful information about the macOS installation process and is easy to parse with command-line tools.
But such flat log files often don’t provide the whole story, and messages logged to them are usually recorded in the unified logging system as well. For a complete picture of what’s happening on a system, you should use the
log command to explore the unified log.
(While we won’t cover it in this guide specifically, it’s worth mentioning that the Console application (in the /Applications/Utilities folder) can also be useful for examining logs if you prefer a graphical interface; however, the
log command is much more flexible and useful in practice. Apple Configurator 2 from the Mac App Store is also useful for streaming the live system log of a connected iOS device, which can be useful for troubleshooting issues as they happen on iOS; Xcode can also be used for that purpose.)
The log Command
log command is built into macOS at
/usr/bin/log. For full details on how to use it, type
man log in Terminal to read the man(ual) page for the command. (Press
q to exit.) In this section, we’ll cover the basics to get you started.
Let’s say you want to see all of the “default” logs on your system for the last minute (excluding extra informational or debug-level messages). To do that, you can run the command:
sudo log show --last 1m
You’ll likely be surprised at the sheer number of messages that appear on your screen just from the last minute. That’s because of Apple’s stated goal of doing as much logging as much of the time as possible. As we’ll explain shortly, you can filter messages, but for now let’s look at some of the common options for the
In the above example, we used
log show. What this does is straightforward: it’s simply showing the logs on the system. We time-limited the list by using
--last 1m (with
m standing for "minute"). You can pass different time modifiers into the
--last 1h (the last hour) or
--last 1d (the last day), for example. With the
--end options plus specific timestamps—in the formats
YYYY-MM-DD HH:MM:SS, or
YYYY-MM-DD HH:MM:SSZZZZZ—you can very narrowly filter the list of messages based on timestamp.
log show command shows the logs of the Mac on which you’re running it. You can also combine
show with the
--archive option for passing in the path to a system log archive generated using the command
log collect. The
collect option is useful for collecting logs from any system—your own or another—for analysis later; they can be stored, moved around, and analyzed at any point in time.
sysdiagnose tool, which runs automatically when you file feedback with Apple, even automatically generates a system log archive for Apple engineers to analyze (named
system_logs.logarchive in the root of the
sysdiagnose folder). You can view more details on the
collect option in the man page for
To see how these building blocks can fit together, consider this example for collecting all of the default level logs on your system for the last 20 minutes:
sudo log collect --output ~/Desktop/SystemLogs.logarchive --last 20m
This will place a log archive file on your desktop named
SystemLogs.logarchive. To view the logs from the archive, you could run:
sudo log show --archive ~/Desktop/SystemLogs.logarchive
log show is useful for looking back in time,
log stream displays logs in real-time. This can be useful for observing logs while an issue is happening or while you’re performing an action on the system and you simply want to watch and learn what’s happening.
For example, to see all of the default logs on your system as they’re happening, use the command:
sudo log stream
(To end the stream, press
Ctrl+C.) Streaming all logs in real-time is not very useful in practice, due to the sheer volume of messages that are generated. Paring down the logs, either when looking back in time with
log show (including with archives) or in real-time using
log stream can be accomplished using predicate filtering.
Apple’s goal to have as much logging on as much of the time as possible is great for admins, because more logging often means more insight that can be used to solve problems or to learn. But it can also make it difficult to find the signal in all the noise. This is where predicate filtering becomes enormously useful.
Apple has a full guide to predicate filtering on its website, and the topic is covered in the man page for the
log command as well. But here’s an example to help get you started:
sudo log stream --debug --predicate 'subsystem=="com.apple.sharing" and category=="AirDrop"'
First, note that we’re streaming the logs (
log stream) and that we’ve also passed in the
--debug option to include more detailed debug logging in the output.
Next, note the
--predicate option. This tells the log command that we want to filter the log messages; what we include between the next set of single quotes becomes the filter. In this example, we’re looking for logs generated by the
com.apple.sharing subsystem that also match the
AirDrop category. This relatively short predicate filter is a great one to keep in your toolbox for looking at AirDrop logs.
Apple defines subsystems as “a major functional area” of an app—or, in this case, the operating system. A category is defined as something that “segregates specific areas within a subsystem”. When you're first starting out filtering logs, it can be difficult to know what criteria to specify in a predicate filter. For example, how did we know that AirDrop uses that particular subsystem and category?
Software vendors can help; they can provide you with the appropriate filters to look for logs generated by their product(s). For example, the Kandji Agent on macOS generates its logs with a subsystem of
io.kandji.KandjiAgent and various categories.
One other way to identify common subsystems and categories is simply to look at the output from
log stream and
log show. Consider a log entry like this:
mdmclient: [com.apple.ManagedClient:OSUpdate] Mapped status SU: [phase: downloading; progress: 0] to MDM: [ProductKey: MSU_UPDATE_21E258_patch_12.3.1; Status: Downloading; IsDownloaded: 0; DownloadPercentComplete: 0]
In this case,
com.apple.ManagedClient is the subsystem,
OSUpdate is the category, and
mdmclient is the process. (Filtering by process is another useful option in predicate filtering.)
The actual log message (
eventMessage) is really useful, too. Based on the example above, if you wanted to filter for just logs that came from the
mdmclient process, were logged with the
com.apple.ManagedClient subsystem with the
OSUpdate category, and contained the string
MSU_UPDATE_21E258_patch_12.3.1 in the messages, you could use the following filter:
process=="mdmclient" and subsystem=="com.apple.ManagedClient" and category=="OSUpdate" and eventMessage contains "MSU_UPDATE_21E258_patch_12.3.1"
Using this predicate would output logs showing the status of a software update to macOS 12.3.1 initiated by an MDM solution. Note that we used
contains; other options for string comparison include:
matches, among others. String comparisons with predicate filters are case and accent (diacritic) sensitive by default.
A few useful processes, subsystems, and categories for Apple admins are listed below. This is by no means a fully inclusive list; note also that Apple may at its discretion change these at any time.
|Apple Push Notification Service (APNs)||process:
|Apple Watch unlock||subsystem:
|macOS Installer and Software Update||process:
|Mobile Activation (Activation Lock, DEP Enrollment, etc.)||process:
|OCSP (Certificate validity)||subsystem:
When reviewing logs, you may see entries that contain the string
<private>, sometimes accompanied by a unique identifier (or UUID). When you see this, something on the system has tried to log data that could potentially identify a user, the network, or another piece of dynamic information that could be considered private in some way. By default, these types of entries are masked by the unified logging system; this ties back to Apple’s goal of making privacy a core pillar of unified logging.
Take this example from Open Directory. This log indicates a successful authentication event, such as when successfully unlocking a preference pane in System Preferences:
opendirectoryd: (PlistFile) [com.apple.opendirectoryd:auth] Authentication succeeded for <private> (1B0820C7-05D9-4DEA-BF67-370FAB16E41C): ODNoError
By default, the user name of the user is masked as
<private>. Sometimes when troubleshooting, however, it can be useful to expose data typically marked as private. You can do this with a configuration profile, optionally delivered by an MDM solution, either for specific subsystems or for the system as a whole. (See the Enable_Private_Data profile examples on the Kandji support GitHub repository.)
Here is the same log entry, but with private data logging enabled for the
com.apple.opendirectoryd subsystem, using the Open Directory private data profile:
opendirectoryd: (PlistFile) [com.apple.opendirectoryd:auth] Authentication succeeded for ladmin (1B0820C7-05D9-4DEA-BF67-370FAB16E41C): ODNoError
Note that this time the username
ladmin was logged.
We recommend enabling private data logging only for specific subsystems, and only when absolutely necessary. To resume hiding private data, simply remove the configuration profile.
log command and predicate filtering, the possibilities for viewing, streaming, collecting, and filtering your logs to find the information you need are vast.
The commands above are just the start of what the log command can do to tell the story of what’s happening on a system. Once you spend some time viewing and reviewing logs, you can start to identify common “rhythms” or patterns, which then make it easier to spot anomalies. The more you learn about them, the more useful logs become for both troubleshooting and for discovering how and why things happen on a system—and that makes the
log command an essential tool for any Apple admin.
The Kandji team is constantly working on solutions to help you deliver great experiences to your users. With powerful and time-saving features such as zero-touch deployment, one-click compliance templates, and plenty more, Kandji has everything you need to manage your Apple fleet in the modern workplace.