Efficiently capture log messages to memory and disk. Manage logging behavior and persistence.
Language
- Objective-C
Overview
About the Logging System
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.
Important
Unified logging is available in iOS 10.0 and later, macOS 10.12 and later, tvOS 10.0 and later, and watchOS 3.0 and later, and supersedes ASL (Apple System Logger) and the Syslog APIs. Historically, log messages were written to specific locations on disk, such as /etc/system.log. The unified logging system stores messages in memory and in a data store, rather than writing to text-based log files.
Log Levels
There are several log levels employed by the unified logging system, as shown in Table 1. These levels 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).
System log levels
Level | Constant | Behavior |
|---|---|---|
Default | 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 | 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 | 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 | 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 | 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. |
Performing Logging
To send a message to the logging system, call an os_log function corresponding to the desired log level. Table 2 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.
Primary Logging functions
Function | Description |
|---|---|
os_log | Sends a default-level message to the logging system. See |
os_log_info | Sends an info-level message to the logging system. See |
os_log_debug | Sends a debug-level message to the logging system. See |
os_log_error | Sends an error-level message to the logging system. |
os_log_fault | Logs a fault-level message to the logging system. |
Logging a default-level message
os_log(OS_LOG_DEFAULT, "This is a log message.");
Logging an info-level message
os_log_info(OS_LOG_DEFAULT, "This is additional info that may be helpful for troubleshooting.");
Logging a debug-level message for a specific subsystem
os_log_t customLogObject;
customLogObject = os_log_create("com.your_company.your_subsystem_name.plist", "your_category_name");
if (customLogObject) {
os_log_debug(customLogObject, "This is info that may be helpful during development or debugging.");
}
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.
Logging a message at a specified level
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_INFO, "This is another way to log an info-level message.");
Important
Log message lines greater than the system’s maximum message length are truncated when stored by the logging system. Complete messages are visible when using the log command-line tool to view a live stream of activity. Bear in mind, however, that streaming log data is an expensive activity.
Privacy
The unified logging system considers dynamic strings and complex dynamic objects to be private, and does not collect them automatically. To ensure the privacy of users, it is recommended that log messages consist strictly of static strings and numbers. In situations where it is necessary to capture a dynamic string, you may explicitly declare the string public using the keyword public. For example, %{public}s.
Formatting Log Messages
To format a log message, use a standard NSString or printf format string, as shown in Listing 5. See String Format Specifiers and printf for formatting rules.
Logging a message using a format string
os_log(OS_LOG_DEFAULT, "Downloaded a file. Size: %zd", fileSize);
In addition to standard format string specifiers, such as %@ and %d, the logging system supports custom decoding of values by denoting value types inline in the format %{value_type}d. In addition, the specifier %.*P can be used to decode arbitrary binary data. The system includes a number of built-in value type decoders, shown in Table 3.
Builtin value type decoders
Value type | Custom specifier | Example output |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Viewing Log Messages
Use the Console app or the log command-line tool to view and filter log messages.
Viewing Log Messages in Console
Console, found in /Applications/Utilities/, includes a number of features designed to make viewing and interpreting log content efficient.
View all available logs in memory.
View a live stream of logging data.
View log data from connected devices.
View log files generated by legacy logging systems, such as ASL and the Syslog APIs.
Use the search field in the toolbar to filter for specific messages by activity, process, message content, subsystem, and more. Advanced search options let you configure search tokens and fine tune filtering behavior to produce more accurate results. See
Figure 1.Save commonly performed searches to the toolbar for quick access in the future.
Enable an activity view to see connections between related messages. See
Figure 2.View the contents of
.logarchivefiles generated by thelogcommand line tool’scollectargument.
Filtering log messages in Console

Viewing related log messages in Console

Viewing Log Messages from the Command Line
The log command-line tool provides options for viewing and filtering log data. Use the command’s show argument and specify the desired filtering options. Listing 6, for example, filters for messages logged by a specific process name. Listing 7 filters for messages logged by a specific subsystem. Listing 8 filters for messages logged by a specific category within a specific subsystem.
Filtering existing processes for a specific process name
$ log show --predicate 'process == "your_process_name"'
Filtering for a specific subsystem
$ log show --predicate 'subsystem == "com.your_company.your_subsystem_name"'
Filtering for a specific category within a specific subsystem
$ log show --predicate 'subsystem == "com.your_company.your_subsystem_name" and category == "your_category_name"'
For a complete list of the log command’s parameters, options, and use case examples, call man log from the command-line.
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 9, which shows how to enable debug-level logging for a subsystem.
Enabling debug-level logging for a subsystem
$ sudo log config --mode "level:debug" --subsystem com.your_company.your_subsystem_name
Use the log tool’s status argument to check the current logging level of a subsystem. See Listing 10.
Checking the log level of a subsystem
$ sudo log config --status --subsystem --subsystem com.your_company.your_subsystem_name
Mode for 'com.your_company.your_subsystem_name' DEBUG
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.your_company.your_subsystem_name.plist. 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 11.
Top level structure of a logging profile
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<!-- GLOBAL SUBSYSTEM OR PROCESS SETTINGS -->
</dict>
<key>CategoryName</key>
<dict>
<!-- CATEGORY SETTINGS -->
</dict>
</dict>
Each settings dictionary within a logging profile contains a Level subdictionary, which contains the following setting keys:
Key | Description |
|---|---|
| Enables a specific log level. |
| Controls whether messages are stored in memory and then saved to the data store, or stored in memory only. |
The Enable key and Persist key both accept the following string values:
Value | Description |
|---|---|
| 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 12 shows an example of a Level subdictionary that enables info-level logging that inherits the persistence behavior of a subsystem or the system.
Example of a Level subdictionary in a logging profile settings dictionary
<key>Level</key>
<dict>
<key>Enable</key>
<string>Info</string>
<key>Persist</key>
<string>Inherit</string>
</dict>
Listing 13 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.
Example of a complete logging profile
<dict>
<key>DEFAULT-OPTIONS</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Info</string>
<key>Persist</key>
<string>Inherit</string>
</dict>
</dict>
<key>server-connections</key>
<dict>
<key>Level</key>
<dict>
<key>Enable</key>
<string>Debug</string>
<key>Persist</key>
<string>Inherit</string>
</dict>
</dict>
</dict>
Note
Subsystems inherit the logging behavior of the system, and categories inherit the behavior of the subsystem in which they reside. Therefore, settings only need to be specified if they differ from the inherited behavior.
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.
Included Headers
<log.h>