Controlling what’s written to the unified log

If there’s one universally consistent impression given by the macOS unified log it’s how little control we have over its unceasing torrent of messages. That’s not entirely true, though: Apple does provide very extensive controls. It’s just that they’re buried in the log command and not particularly well documented given their complexity. This article explains as much as I understand: with limited information, I’m sure I’ve made some mistakes, and welcome all corrections.

Options for the collection of different log messages are set in logging profiles, XML property lists stored in /System/Library/Preferences/Logging (on the System volume, so read only) and /Library/Preferences/Logging (which the user controls). You can create your own custom profiles, or modify them on the fly using the log command. As far as I can see, that command makes immediate changes and records them into logging profiles, whereas working directly with profiles may require restarting to bring them into effect.

To get information about the current profile for a subsystem, use a command of the form
sudo log config --status --subsystem com.apple.iohid
which returns level and persist status for the specified subsystem. You can instead specify a process ID using
--process [pid]
or refine subsystem with a category using
--category [category name]

Setting a new level or persist mode is performed using a command of the form
sudo log config --mode "level:debug" --subsystem com.apple.iohid
which sets the level for that subsystem to Debug, ensuring that all messages of Debug or higher level are stored in the log.

Logging level Enable determines which classes of log entry are written to the log, and are hierarchical. Set the level to Debug and all messages will be saved; with Info as the level, Debug messages won’t appear at all. Persist determines whether those log entries are written from memory to the log file for future reference, and uses the same set of options. Set the Persist to Info and Debug messages may still be collected to memory, but they won’t be written to log files in storage.

If you don’t specify a process or subsystem, then log assumes that this will be applied as system-wide defaults, which are in turn inherited by all others which don’t have their own custom settings.

Apple envisages this primarily being used by developers to control logging levels for their own apps, as described here. However, those commands and logging profiles also apply to system processes, particularly in their custom profiles. Here I presume that settings given in /Library/Preferences/Logging override those in /System/Library/Preferences/Logging.

Keys available in logging profiles are extensive, and detailed in the Appendix. If you want to construct your own profiles, refer to those on the System volume for examples of their structure and conventions. The keys listed in the Appendix are those most commonly encountered in Apple’s supplied profiles, but they may be more extensive than I have given. Additional information is provided by man log, and here.

Logging profiles don’t provide you with the ability to mute much of the chatter that goes on in the log, but can be very useful at times. Remember that any custom profiles will also affect what is saved in logarchives, including those saved in sysdiagnose output. Before making a sysdiagnose, perhaps to send to Apple as part of a bug report, you should disable those customisations.

Appendix: Key-value pairs in logging profiles

Processes: named using process ID e.g. com.myco.appname
Subsystems: named using the same subsystem name as used in the log entries
Profiles are named using either process or subsystem names, with the .plist extension, and placed in the appropriate folder.

logprofiles

Each profile consists of one or more top-level keys, consisting of the category name or DEFAULT-OPTIONS.
Top level keys: [category name], DEFAULT-OPTIONS
Values: <dict> or an empty dictionary <dict/>

Second-level keys include Level, TTL, and Event-Log, each of which have their own dictionaries, and the following simple keys:
Signpost-Scope value: String = {Process, System}
Signpost-Enabled value Boolean
Signpost-Persisted value Boolean
Default-Privacy-Setting value: String = {Public, Private} In theory, this could override the privacy settings for the whole log.
Enable-Oversize-Messages value Boolean This enables long multi-line entries to be included
Enable-Fault-Crashlogs value Boolean
Propagate-with-Activity value Boolean

Level value: dictionary
keys:
Enable value: String = {Inherit, Default, Info, Debug, Off} sets the level of entries saved in the log
Persist value: String = {Inherit, Default, Info, Debug, Off} determines whether the entry is stored in the log files inside the Persist folder

TTL value: dictionary these set the ‘times to live’, the period for which those entries are retained, in days
keys:
Debug value: Integer
Default value: Integer
Info value: Integer

Event-Log value: dictionary
key:
Enabled value: {String = Inherit}

Boolean values are <false/> and <true/>