Efficiently capture log messages to memory and disk. Manage logging behavior and persistence.
The unified logging system provides a single, efficient, performant API for capturing messaging across all levels of the system. This unified system centralizes the storage of log data in memory and in a data store on disk. The system implements global settings that govern logging behavior and persistence, while at the same time providing fine-grained control during debugging via the
log command-line tool and through the use of custom logging configuration profiles. Log messages are viewed using the Console app in
/Applications/Utilities/ and the
log command-line tool. Logging and activity tracing are integrated to make problem diagnosis easier. If activity tracing is used while logging, related messages are automatically correlated.
There are several log levels employed by the unified logging system, which correspond to the different types of messages your app may need to capture, and define when messages are saved to the data store and how long they persist. The system implements standard behavior for each level. This behavior can be overridden using the log command-line tool or a custom configuration profile (see Customizing Logging Behavior While Debugging).
Default-level messages are initially stored in memory buffers. Without a configuration change, they are compressed and moved to the data store as memory buffers fill. They remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Use this level to capture information about things that might result a failure.
Info-level messages are initially stored in memory buffers. Without a configuration change, they are not moved to the data store and are purged as memory buffers fill. They are, however, captured in the data store when faults and, optionally, errors occur. When info-level messages are added to the data store, they remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Use this level to capture information that may be helpful, but isn’t essential, for troubleshooting errors.
Debug-level messages are only captured in memory when debug logging is enabled through a configuration change. They’re purged in accordance with the configuration’s persistence setting. Messages logged at this level contain information that may be useful during development or while troubleshooting a specific problem. Debug logging is intended for use in a development environment and not in shipping software.
Error-level messages are always saved in the data store. They remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Error-level messages are intended for reporting process-level errors. If an activity object exists, logging at this level captures information for the entire process chain.
Fault-level messages are always saved in the data store. They remain there until a storage quota is exceeded, at which point, the oldest messages are purged. Fault-level messages are intended for capturing system-level or multi-process errors only. If an activity object exists, logging at this level captures information for the entire process chain.
To send a message to the logging system, call an
os_log function corresponding to the desired log level. Table 1 shows the main
os_log functions. Provide a log object—the OS_LOG_DEFAULT constant or a custom log object created by calling the
os_log_create function—and a constant string or format string representing the message. The
OS_LOG_DEFAULT constant causes logging to occur in accordance with the system’s standard behavior. A custom log object causes logging to occur according to settings contained within a logging profile for a specific subsystem. Calling an
os_log function doesn’t always guarantee that a message is captured. This is governed by the current logging configuration for the level of message being sent.
Sends a default-level message to the logging system. See Listing 1.
Sends an info-level message to the logging system. See Listing 2.
Sends a debug-level message to the logging system. See Listing 3. Note that debug-level messages are only captured when debug logging has been explicitly enabled via the
Sends an error-level message to the logging system.
Logs a fault-level message to the logging system.
Messages can also be sent to the logging system by calling the os_log_with_type function and providing a log object, a log type constant (see
os_log_type_t), and a message. In Listing 4, the
os_log_with_type function sends an info-level message to the logging system.
Viewing Log Messages
Use the Console app or the
log command-line tool to view and filter log messages.
Customizing Logging Behavior While Debugging
Logging behavior is normally governed by the system. However, while debugging in macOS, you can enable different logging levels for a subsystem using the
log command-line tool’s
config argument while logged in as root. See Listing 5, which shows how to enable debug-level logging for a subsystem.
status argument to check the current logging level of a subsystem. See Listing 6.
You can also override the logging behavior of a specific subsystem by creating and installing a logging configuration profile property list file in the
/Library/Preferences/Logging/Subsystems/ directory. Name the file using an identifier string, in reverse DNS notation, representing the subsystem. For example,
com. Next, add one or more settings dictionaries to the top level of the file. A
DEFAULT-OPTIONS settings dictionary defines global behavior settings for the entire subsystem. Category settings dictionaries define behavior for specific categories of messages within the subsystem. See Listing 7.
Each settings dictionary within a logging profile contains a
Level subdictionary, which contains the following setting keys:
Enables a specific log level.
Controls whether messages are stored in memory and then saved to the data store, or stored in memory only.
Enable key and
Persist key both accept the following string values:
Explicitly states that the subsystem or category inherits the behavior of its parent. In the case of a category, the parent is the subsystem. In the case of a subsystem, the parent is the system.
Only default-level messages are captured.
Default-level and info-level messages are captured.
Default-level, info-level, and debug-level messages are captured.
Listing 8 shows an example of a
Level subdictionary that enables info-level logging that inherits the persistence behavior of a subsystem or the system.
Listing 9 shows an example of a complete logging profile, which configures a subsystem to perform info-level logging and a
server-connections category within the subsystem to perform debug-level logging.
Logging Best Practices
Follow these guidelines to produce useful and efficient log messages.
Use format strings and specifiers whenever possible to automatically produce user-friendly log messages instead of trying to write custom formatting code. See Formatting Log Messages.
Don’t include symbolication information or source file line numbers in messages. The system automatically captures this information.